﻿/*
 * Copyright (c) HiSilicon (Shanghai) Technologies Co., Ltd. 2023-2023. All rights reserved.
 */

#include "BurnCtrlDfu.h"
#include <QFileInfo>
#include <QThread>
#include <QMutexLocker>
#include <setupapi.h>
#include <cfgmgr32.h>
#include <usbioctl.h>
#include <setupapi.h>
#include <initguid.h>
#include <devguid.h>
#include "SecureLink/Channel.h"
#include "fcntl.h"
#include "BurnToolCommon/portable.h"
#include "../platform/Securec/securec.h"
extern "C" {
#include <hidsdi.h>
};
using namespace UsbDfu;
using namespace BurnCtrlDfuClass;

namespace {
    const int LMDFU_ADDRESS_LENGTH = 1024;
    const int DFU_TIMEOUT_RET = 5000;
    const int DFU_DELETE_LENGTH = 4;
    const int TWO_STOP = 2;
    const int THREE_STOP = 3;
    const int FOUR_STOP = 4;
    const int FIVE_STOP = 4;
    const int SIX_STOP = 6;
    const int MILLI_SLEEP_TIME = 1000;
    const int LIST_LEN_MINI_VALUE = 2;
    const int VALUE_RANGE_FOUR = 4;
    const int VALUE_RANGE_TWO = 2;
    const int OUTLEN_DELETE_LENGTH = 2;
    const int TBUF_ADD_LENGTH = 2;
    const int NUMBER_ADD = 2;
    const int FUNC_DFU_BLENGTH = 7;
    const int FUNC_DFU_BLENGTH_RANGE_NINE = 9;
    const int BINTERFACEPROTOCOL_VALUE_TWO = 2;
    const int POINTER_OFFSET_EIGHT = 8;
    const int TRANSACTION_VALUE_TWO = 2;
    const int NUMBER_1K = 1000;
    const int WLENGTH_SIX = 6;
    const int BURNCTRL_DFU_NUM_2 = 2;
    const int BURNCTRL_DFU_NUM_3 = 3;
    const int BURNCTRL_DFU_NUM_4 = 4;
    const int BURNCTRL_DFU_NUM_5 = 5;
    const int BURNCTRL_DFU_NUM_6 = 6;
    const int BURNCTRL_DFU_NUM_7 = 7;
    const int BURNCTRL_DFU_NUM_8 = 8;
    const int BURNCTRL_DFU_NUM_16 = 16;
    const int BURNCTRL_DFU_NUM_24 = 24;
    const int BURNCTRL_DFU_NUM_3F = 0x3f;
    const char* DFUSE_COMMAND_NAME[] = {"SET_ADDRESS", "ERASE_PAGE", "MASS_ERASE", "READ_UNPROTECT"};
    const int DFU_SUFFIX_LENGTH = 16;
    const int LMDFU_PREFIX_LENGTH = 8;
    const int LPCDFU_PREFIX_LENGTH = 16;
    const int PROGRESS_BAR_WIDTH = 25;
    const int STDIN_CHUNK_SIZE = 65536;
    const int NUM_TEN = 10;
    const int SLEEP_TIME = 500;
    const int RETRY_TIMES = 60;
    const int AUTO_DFU_SLEEP_TIME = 20;
    const int AUTO_DFU_RETRY_TIMES = 500;
    const int MULTIPLE_AUTO_DFU_SLEEP_TIME = 100;
    const int MULTIPLE_AUTO_DFU_RETRY_TIMES = 600;
    const int MULTIPLE_USB_OPEN_SLEEP_TIME = 50;
    const int MULTIPLE_USB_OPEN_RETRY_TIMES = 20;
    const int SEND_FILE_SIZE = 1024  + 1;
    const int NUM5 = 5;
    const int NUM10 = 10;
    const int NUM6000 = 6000;
    const int USB_SERIAL_UNKNOWN = -1;
    const int OTHER_THREAD_OCCUPY = -2;
    const int USB_DT_DFU = 0x21;
    const int DFU_STATUS_ERR_STALLEDPKT = 0x0f;
    const int NUMBER_1 = 1;
    const int NUMBER_2 = 2;
    const int NUMBER_3 = 3;
    const int NUMBER_4 = 4;
    const int NUMBER_6 = 6;
    const int NUMBER_8 = 8;
    const int NUMBER_12 = 12;
    const int NUMBER_16 = 16;
    const int NUMBER_256 = 256;
    const int TIMEOUT_WRITE = 50000;
    const int DIE_ID_FIRST_BYTE = 0xF7;
    const int DIE_ID_LAST_BYTE = 0x1F;

    const char *DFU_STATUS_NAME[] = {
        /* DFU_STATUS_OK */
        "No error condition exists.",
        /* DFU_STATUS_errTARGET */
        "File is not for use by this device.",
        /* DFU_STATUS_errFILE */
        "File is for this device but fails some vendor-specific tests.",
        /* DFU_STATUS_errWRITE */
        "Device is unable to write memory.",
        /* DFU_STATUS_errERASE */
        "Memory erase function failed.",
        /* DFU_STATUS_errCHECK_ERASED */
        "Memory erase check failed.",
        /* DFU_STATUS_errPROG */
        "Memory programing failed.",
        /* DFU_STATUS_errVERIFY */
        "Failed to verify programmed memory.",
        /* DFU_STATUS_errADDRESS */
        "Cannot program memory due to received address that is out of range.",
        /* DFU_STATUS_errNOTDONE */
        "Received DFU_DNLOAD with wLength = 0, but the device does not think that it has all data yet.",
        /* DFU_STATUS_errFIRMWARE */
        "Device's firmware is corrupt, and cannot return to runtime (non-DFU) operations.",
        /* DFU_STATUS_errVENDOR */
        "iString indicates a vendor specific error.",
        /* DFU_STATUS_errUSBR */
        "Unexpected USB reset signalling detected by device.",
        /* DFU_STATUS_errPOR */
        "Unexpected power on reset detected by device.",
        /* DFU_STATUS_errUNKNOWN */
        "An unknown error occurred.",
        /* DFU_STATUS_errSTALLEDPKT */
        "Device stalled an unexpected request."
    };
}

BurnCtrlDfu::BurnCtrlDfu(ChipType type, QObject *parent)
    : BurnCtrl(type, parent),
      dfuRoot(nullptr),
      ctx(nullptr)
{
    this->isAutoBurn = true;
    isEraseMode = 0;
    file.name = nullptr;
}

BurnCtrlDfu::~BurnCtrlDfu()
{
    if (file.name != nullptr) {
        delete[] file.name;
        file.name = nullptr;
    }
    if (file.firmware != nullptr) {
        free(file.firmware);
        file.firmware = nullptr;
    }
}

void BurnCtrlDfu::StartConnect()
{
    m_serialName.clear();
    m_configPort.clear();

    isOpen = true;
    Connect();
}

void BurnCtrlDfu::ResetUIAndFSM()
{
    if (dfuRoot && dfuRoot->devHandle != nullptr) {
        libusb_close(dfuRoot->devHandle);
        dfuRoot->devHandle = nullptr;
        DisconnectDevices();
        libusb_exit(ctx);
        ctx = nullptr;
    }

    isProcessOk = false;
    BurnCtrl::ResetUIAndFSM();
    oneBurnTime = 0;
    isShutDown = false;

    dfuTimeout = DFU_TIMEOUT_RET;
    lastErasedPage = 1;
    mDfuseAddress = 0;
    mDfuseAddressPresent = 0;
    mDfuseLength = 0;
    mDfuseForce = 0;
    mDfuseLeave = 0;
    mDfuseUnprotect = 0;
    mDfuseMassErase = 0;
    mDfuseWillReset = 0;
    verbose = 0;
    dfuRoot = nullptr;
    matchVendor = -1;
    matchProduct = -1;

    mTransferSize = TRANSFER_SIZE;
    m_mode = DfuMode::MODE_NONE;
    ctx = nullptr;
    retValue = 0;
    errno_t retMemset = memset_s(&file, sizeof(DfuFile), 0, sizeof(DfuFile));
    if (retMemset != 0) {
        qDebug() << "Error memset";
    }
    dfuseDevice = 0;
    mDfuseOptions = nullptr;
    runtimeVendor = 0;
    runtimeProduct = 0;
    transaction = 0;
    burnFileSize = 0;
    hidDeviceHandle = nullptr;
}

void BurnCtrlDfu::SetEraseMode(int eraseMode)
{
    isEraseMode = eraseMode;
}

void BurnCtrlDfu::ResetByCmd()
{
    SEBOOT_RESET st;
    st.GetCrc();
    WriteToPort(reinterpret_cast<char *>(&st), st.packetSize);
}

bool BurnCtrlDfu::ConfigComPort()
{
    return true;
}

bool BurnCtrlDfu::IsBusy()
{
    return isConnect;
}

int BurnCtrlDfu::Connect()
{
    if (ConnectModeHidDfu() == 0) {
        return 0;
    }
    retValue = libusb_init(&ctx);
    if (retValue != 0) {
        ERRX(PortableErro::EX_IOERR, "unable to initialize libusb: %s", libusb_error_name(retValue));
    }
    if (dfuMode == DfuBurnMode::AUTO_DFU) {
        QString log = tr("Reset the device...\r\n");
        OutputString(log);
    }
    int dfuRootCount = 0;
    while (dfuRoot == nullptr) {
        if (Global::IsUSBType(chipType) &&
            (dfuRootCount % NUM_TEN == 0) && (dfuMode == DfuBurnMode::DFU)) {
            EnterDfuMode();
        }
        if (isShutDown) {
            return ERR_SOP_CPAB_NOT_CONNECT_UE;
        }
        int sleepTime = 0;
        int retryTimes = 0;
        if (isMultipleBurn) {
            sleepTime = MULTIPLE_AUTO_DFU_SLEEP_TIME;
            retryTimes = MULTIPLE_AUTO_DFU_RETRY_TIMES;
        } else if (dfuMode == DfuBurnMode::AUTO_DFU) {
            sleepTime = AUTO_DFU_SLEEP_TIME;
            retryTimes = AUTO_DFU_RETRY_TIMES;
        } else {
            sleepTime = SLEEP_TIME;
            retryTimes = RETRY_TIMES;
        }
        if (dfuRootCount > retryTimes) {
            break;
        }
        Sleep(sleepTime);
        ProbeDevices(ctx);

        if (dfuMode == DfuBurnMode::DFU) {
            PRINTFX("Retrying.\n", "");
        }
        dfuRootCount++;
    }

    if (dfuRoot == nullptr) {
        libusb_exit(ctx);
        ctx = nullptr;
        PRINTFX("Driver may not be installed. Open the device manager to confirm.", "");
        ERRX(PortableErro::EX_IOERR, "No DFU capable USB device available.", "");
    } else if (file.bcdDFU == 0x11a && DfuseMultipleAlt(*dfuRoot) != 0) {
        SetEditErrorString("Multiple alternate interfaces for DfuSe file.\n.");
    } else if (dfuRoot->next != nullptr) {
        /* We cannot safely support more than one DFU capable device
         * with same vendor/product ID, since during DFU we need to do
         * a USB bus reset, after which the target device will get a
         * new address */
        ERRX(PortableErro::EX_IOERR, "More than one DFU capable USB device found. Try '--list' and specify"
                                     "the serial number or disconnect all but one device.\n", "");
    }

    /* We have exactly one device. Its libusb_device is now in dfuRoot->dev */
    return PreparationForBurning();
}

int BurnCtrlDfu::ConnectModeHidDfu()
{
    if (dfuMode == DfuBurnMode::HID_DFU) {
        QString log = tr("Reset the device...\r\n");
        OutputString(log);
        unsigned int count = 0;
        while (hidDeviceHandle == nullptr) {
            if (isCancel == TRUE) {
                return 0;
            }
            if (count > AUTO_DFU_RETRY_TIMES) {
                ERRX(PortableErro::EX_IOERR, "No DFU capable USB device available", "");
            }
            ConnectHidDfu();
            Sleep(AUTO_DFU_SLEEP_TIME);
            count++;
        }

        emit SignalSendMsg(static_cast<long>(WiteThreadMsg::WITE_THREAD_MSG_COMMON_ACTION2), 0, 0);
        return 0;
    } else {
        return 1;
    }
}

int BurnCtrlDfu::PreparationForBurning()
{
    PRINTFX("Opening DFU capable USB device...\n", "");
    int retryTimes = 0;
    retValue = libusb_open(dfuRoot->dev, &dfuRoot->devHandle);
    while ((retValue != 0 || !dfuRoot->devHandle) && retryTimes < MULTIPLE_USB_OPEN_RETRY_TIMES) {
        Sleep(MULTIPLE_USB_OPEN_SLEEP_TIME);
        retValue = libusb_open(dfuRoot->dev, &dfuRoot->devHandle);
        retryTimes++;
    }

    if (retValue != 0 || !dfuRoot->devHandle) {
        ERRX(PortableErro::EX_IOERR, "Cannot open device: %s", libusb_error_name(retValue));
    }

    PRINTFX("Device ID %04x:%04x\n", dfuRoot->vendor, dfuRoot->product);
    hidDeviceInfo = deviceInfo;
    deviceInfo.vid = dfuRoot->vendor;
    deviceInfo.pid = dfuRoot->product;
    deviceInfo.serialName = QString::fromLatin1(dfuRoot->serialName);
    /* If first interface is DFU it is likely not proper run-time */
    if (dfuRoot->target > 0) {
        PRINTFX("Runtime device", "");
    } else {
        PRINTFX("Device", "");
    }
    PRINTFX(" DFU version %04x\n", libusb_le16_to_cpu(dfuRoot->funcDfu.bcdDFUVersion));

    if ((dfuRoot->flags & DFU_IF_F_DFU) == 0) {
        runtimeVendor = dfuRoot->vendor;
        runtimeProduct = dfuRoot->product;
        PRINTFX("Claiming USB DFU (runtime) interface...\n", "");
        retValue = libusb_claim_interface(dfuRoot->devHandle, dfuRoot->target);
        if (retValue < 0) {
            ERRX(PortableErro::EX_IOERR, "Cannot claim interface %d: %s", dfuRoot->target, libusb_error_name(retValue));
        }
        if ((dfuRoot->target > 0) || (dfuRoot->flags & DFU_IF_F_ALT) != 0) {
            PRINTFX("Setting alternate interface zero...\n", "");
            retValue = libusb_set_interface_alt_setting(dfuRoot->devHandle, dfuRoot->target, 0);
            if (retValue < 0) {
                ERRX(PortableErro::EX_IOERR, "Cannot set alternate interface zero: %s", libusb_error_name(retValue));
            }
        }
        PRINTFX("Determining device status...\n", "");
        return ConnectSub();
    } else {
        if (PartOfPreparationForBurning() != 0) {
            return retValue;
        }
    }
    return DfuStateAction();
}

int BurnCtrlDfu::PartOfPreparationForBurning()
{
    runtimeVendor = matchVendor < 0 ? dfuRoot->vendor : matchVendor;
    runtimeProduct = matchProduct < 0 ? dfuRoot->product : matchProduct;
    libusb_release_interface(dfuRoot->devHandle, dfuRoot->target);
    libusb_close(dfuRoot->devHandle);
    dfuRoot->devHandle = nullptr;
    DisconnectDevices();
    retValue = WaitDfuDetach();
    if (retValue != 0) {
        return retValue;
    }
    return 0;
}

void BurnCtrlDfu::EnterDfuMode()
{
    HidEnumerate();
}

int BurnCtrlDfu::ConnectSub()
{
    int err = DfuGetStatus(dfuRoot, dfuStatus);
    if (err == LIBUSB_ERROR_PIPE) {
        PRINTFX("Device does not implement get_status, assuming appIDLE.\n", "");
        dfuStatus.bStatus = DFU_STATUS_OK_VALUE;
        dfuStatus.bwPollTimeout = 0;
        dfuStatus.bState = static_cast<unsigned char>(DfuState::DFU_STATE_APP_IDLE);
        dfuStatus.iString = 0;
    } else if (err < 0) {
        ERRX(PortableErro::EX_IOERR, "error get_status: %s", libusb_error_name(err));
    } else {
        PRINTFX("DFU state(%u) = %s, status(%u) = %s\n", dfuStatus.bState, DfuStateToString(dfuStatus.bState),
                dfuStatus.bStatus, DfuStatusToString(dfuStatus.bStatus));
    }
    MILLI_SLEEP(dfuStatus.bwPollTimeout);
    DfuState dfu = static_cast<DfuState>(dfuStatus.bState);
    switch (dfu) {
        case DfuState::DFU_STATE_APP_IDLE:
        case DfuState::DFU_STATE_APP_DETACH:{
            PRINTFX("Device in runtime mode. Send DFU detach request...\n", "");
            if (DfuDetach(dfuRoot->devHandle, dfuRoot->target, NUMBER_1K) < 0) {
                WARNX("Error detaching", "");
            }
            if (Global::IsUSBType(chipType)) {
                CycleUsbDevice(); // <-- your device instance ID here
            }
            if ((dfuRoot->funcDfu.bmAttributes & (1 << 3)) != 0) { // 3
                PRINTFX("Device will detach and reattach...\n", "");
            } else {
                PRINTFX("Resetting USB...\n", "");
                retValue = libusb_reset_device(dfuRoot->devHandle);
                if (retValue < 0 && retValue != LIBUSB_ERROR_NOT_FOUND) {
                    ERRX(PortableErro::EX_IOERR, "Error resetting after detach: %s",
                         libusb_error_name(retValue));
                }
            }
            break;
        }
        case DfuState::DFU_STATE_DFU_ERROR:
            PRINTFX("DFU error, clearing status\n", "");
            if (DfuClearStatus(dfuRoot->devHandle, dfuRoot->target) < 0) {
                ERRX(PortableErro::EX_IOERR, "Error clear_status", "");
            }
            /* fall through */
        default:
            WARNX("Warning: Device already in DFU mode? (bState=%d %s)", dfuStatus.bState,
                  DfuStateToString(dfuStatus.bState));
            libusb_release_interface(dfuRoot->devHandle, dfuRoot->target);
            return DfuStateAction();
    }
    libusb_release_interface(dfuRoot->devHandle, dfuRoot->target);
    libusb_close(dfuRoot->devHandle);
    dfuRoot->devHandle = nullptr;
    /* keeping handles open might prevent re-enumeration */
    DisconnectDevices();
    retValue = WaitDfuDetach();
    if (retValue != 0) {
        return retValue;
    }
    return DfuStateAction();
}


void BurnCtrlDfu::LoadFile(QString filePath, unsigned int index, unsigned int size)
{
    QByteArray array = filePath.toUtf8();
    unsigned int fileNameSize = array.size() + 1;
    CHAR *fileName = new CHAR[fileNameSize]();
    if (fileName == nullptr) {
        return;
    }
    errno_t retMemcpy = memcpy_s(fileName, fileNameSize, array.data(), array.size());
    if (retMemcpy != 0) {
        qDebug() << "Error memcpy";
        return;
    }
    fileName[fileNameSize - 1] = '\0';
    if (file.name != nullptr) {
        delete[] file.name;
    }
    file.name = fileName;
    DfuLoadFile(file, SuffixReq::MAYBE_SUFFIX, PrefixReq::MAYBE_PREFIX, index, size);
}

void BurnCtrlDfu::SetMode(DfuMode mode)
{
    m_mode = mode;
}

void BurnCtrlDfu::CommonAction1()
{
    if (isOpen) {
        if (WaitDfuDetach() == 0) {
            DfuStateAction();
        }
    }
}

void BurnCtrlDfu::CommonAction2()
{
    U32 ret = ERR_SOP_SUCCESS;
    if (burnCmdArray.size() == 0) {
        return;
    }
    if (burnCmdArray.size() < burnCmdIndex) {
        burnCmdIndex = 0;
    }
    BYTE payload[100] = { 0 }; // mac cmd len 100
    U32 len = 0;
    transaction = 0;
    if (infoArray.size() > burnCmdIndex) {
        SetEditErrorString("\r\n" + infoArray[burnCmdIndex].fileName);
    }
    DataConvertor::ByteStr2Buf(burnCmdArray.at(burnCmdIndex), reinterpret_cast<PBYTE>(payload), len);

    if ((this->chipType == ChipType::USB4GCAT1) || (this->chipType == ChipType::USBHI3322) ||
        (this->chipType == ChipType::USBBS27) ||
        (GetCurrentCmdType() != BurnCtrl::LOADER && burnCmdArray.at(burnCmdIndex) != "\n")) {
        WriteToPort(reinterpret_cast<char *>(payload), len); // 发送数据
    }

    oneBurnTime++;
    CommonAction2Sub(ret);
    return;
}

void BurnCtrlDfu::CommonAction2Sub(U32 &ret)
{
    if (GetCurrentCmdType() == BurnCtrl::BURN_CMD) {
        ret = StartBurn();
        if (ret == ERR_DFU_WRITE_FAIL) {
            if (burnCmdReCount == NUM10) {
                burnCmdReCount = 0;
                emit SignalSendMsg(static_cast<long>(WiteThreadMsg::WITE_THREAD_MSG_COMMON_ACTION3), 0, 0);
                return;
            }
            burnCmdReCount++;
            emit SignalSendMsg(static_cast<long>(WiteThreadMsg::WITE_THREAD_MSG_COMMON_ACTION2), 0, 0);
        } else {
            burnCmdReCount = 0;
            burnCmdIndex++;
        }
        if (ret == 0 && burnCmdArray.size() >= burnCmdIndex) {
            emit SignalSendMsg(static_cast<long>(WiteThreadMsg::WITE_THREAD_MSG_COMMON_ACTION2), 0, 0);
        }
    } else if (GetCurrentCmdType() == BurnCtrl::LOADER) {
        SetResult(ERR_DOING);
        SetProgressRange();
        if (chipType == ChipType::USB4GCAT1 || chipType == ChipType::USBHI3322 || chipType == ChipType::USBBS27) {
            ret = StartBurn();
            burnCmdIndex++;
            if (ret == 0 && burnCmdArray.size() > burnCmdIndex) {
                emit SignalSendMsg(static_cast<long>(WiteThreadMsg::WITE_THREAD_MSG_COMMON_ACTION3), 0, 0);
            }
            return;
        }
        burnCmdIndex++;
        if (burnCmdArray.size() > burnCmdIndex) {
            emit SignalSendMsg(static_cast<long>(WiteThreadMsg::WITE_THREAD_MSG_COMMON_ACTION2), 0, 0);
        }
    } else if (GetCurrentCmdType() == BurnCtrl::END) {
        CommonAction2SubAdd();
    }
}

void BurnCtrlDfu::CommonAction2SubAdd()
{
    if (isOneTimeBurn != 0) {
        int totalFileSize = 0;
        for (int i = 0; i < infoArray.size(); i++) {
            totalFileSize += DataConvertor::Str2U32DecHex(infoArray[i].fileSize);
        }
        UpdateProgress(static_cast<U32>(totalFileSize)); // 1024 bytes each package
        if (chipType == ChipType::USB4GCAT1 || chipType == ChipType::USBHI3322) {
            SetEditErrorString(stringClass.GetUsbBurnMode());
            SetEditErrorString(stringClass.GetEraseMode().arg(m_eraseModeStr[isEraseMode]));
            if (chipType == ChipType::USB4GCAT1) {
                // 4GCAT1 USB命令行模式下输出Die ID信息
                SetEditErrorString(QString("ChipId:%1").arg(this->SerialNameToDieID(m_serialName)));
            }
        }
        SetEditErrorString(IDS_STRING_32879);
        SetEditErrorString(stringClass.GetIdsString32880());
        SetEditErrorString(IDS_STRING_32879);
        if (isReset) {
            SetEditErrorString("Reset device");
            ResetByCmd();
        }
        SetResult(0);
        Global::CloseApp(this->result);
        return;
    }

    SetResult(0);
    burnCmdIndex++;
    WifiState();
    SignalBurnFileFinish();
    ResetUIAndFSM();

    // 工厂模式烧写完成后继续检查便于下次开始烧写
    if ((this->chipType == ChipType::USB4GCAT1 || chipType == ChipType::USBHI3322) && this->isMultipleBurn) {
        StartConnect();
    }
}

// 烧写完loader后，继续检测DFU设备
void BurnCtrlDfu::CommonAction3()
{
    if (dfuMode == DfuBurnMode::HID_DFU) {
        CloseHandle(hidDeviceHandle);
        hidDeviceHandle = 0;
        Sleep(1000); // 烧完loader后等1000ms
        Connect();
        return;
    }
    // 停掉上次连接
    if (dfuRoot && dfuRoot->devHandle != nullptr) {
        libusb_close(dfuRoot->devHandle);
        dfuRoot->devHandle = nullptr;
        DisconnectDevices();
        libusb_exit(ctx);
        ctx = nullptr;
    }

    // 重新连接，触发继续烧写。
    Connect();
}

U32 BurnCtrlDfu::StartBurn()
{
    if (!isConnect) {
        return ERR_SOP_CPAB_NOT_CONNECT_UE;
    }

    SetFileInfo(GetCurrentFilePath());

    QFileInfo fileInfo(strFilePath);
    strFileName = fileInfo.fileName();
    fileSize = fileInfo.size();
    strFileSize = QString::number(fileSize);
    if (fileSize <= 0) {
        return ERR_SOP_FILE_NOT_FOUND;
    }
    LoadFile(strFilePath, DataConvertor::Str2U32DecHex(infoArray[burnCmdIndex].fileIndex),
        DataConvertor::Str2U32DecHex(infoArray[burnCmdIndex].fileSize));
    retValue = DfuloadDoDnload(dfuRoot, mTransferSize, file);
    return retValue;
}

U32 BurnCtrlDfu::SendBurnCmd()
{
    SetEditErrorString(stringClass.GetIdsString32882());
    CommonAction2();
    return 0;
}

void BurnCtrlDfu::InitBurnCmdByArray(const QMap<int, BIN_INFO> &infoArray)
{
    burnCmdArray.clear();
    burnCmdTypeArray.clear();
    burnCmdIndex = 0;
    QMap<int, BIN_INFO> tempArray;
    filePathIndex = 0;
    QString strSendData;
    bool isEraseAll = false;
    for (int i = 0; i < infoArray.size(); i++) {
        BIN_INFO info = infoArray[i];
        switch (info.type) {
            case IMAGE_TYPE::IMAGE_TYPE_LOADER: {
                LoaderSendPayload(strSendData, tempArray, info, i);
                break;
            }
            default: {
                if (!isOnlyEraseAll) {
                    AddCmdByInfo(info, isEraseAll, tempArray);
                    SetFilePath(i, info.path);
                }
                break;
            }
        }
    }
    strSendData.clear();
    this->infoArray = tempArray;
    if (isAutoBurn) {
        if (isOnlyEraseAll) {
            OnlyEraseAllSendPayload(strSendData);
        } else {
            NotOnlyEraseAllSendPayload(strSendData);
        }
        burnCmdTypeArray[burnCmdTypeArray.size()] = BurnCtrl::END;
        burnCmdArray.push_back(strSendData);
        strFilePathArray[strFilePathArray.size()] = "";
    }
    burnCmdIndex = 0;
}

void BurnCtrlDfu::LoaderSendPayload(QString &strSendData, QMap<int, BIN_INFO> &tempArray,
                                    const BIN_INFO &info, int index)
{
    if (this->chipType != ChipType::USB4GCAT1 && this->chipType != ChipType::USBHI3322 &&
        this->chipType != ChipType::USBBS27) {
        isWriteLoader = false;
        strSendData = "\n";
        burnCmdArray.push_back(strSendData);
    } else {
        isWriteLoader = true;
        QString strSendOTP = "";
        SEBOOT_DOWNLOAD_OTP_EFUSE st;
        st.startFlag = 0xf5e6d7c8;
        st.frameType = 0xe1;
        st.frameTypeReverse = 0x1e;
        st.fileLen = DataConvertor::Str2U32DecHex(info.fileSize);
        st.checkSum = ChannelClass::Channel::SopCrc16(reinterpret_cast<PBYTE>(&st), st.packetSize - 2); // crc16 len 2
        DataConvertor::Buf2ByteStr(reinterpret_cast<PBYTE>(&st), st.packetSize, strSendOTP);
        burnCmdArray.push_back(strSendOTP);
    }

    burnCmdTypeArray[index] = (BurnCtrl::LOADER);
    SetFilePath(index, info.path);
    tempArray[index] = info;
}

void BurnCtrlDfu::NotOnlyEraseAllSendPayload(QString &strSendData) const
{
    if ((dfuMode == DfuBurnMode::AUTO_DFU || dfuMode == DfuBurnMode::HID_DFU)) {
        if (this->isMultipleBurn) {
            SEBOOT_SWITCH_DFU exitDfu;
            exitDfu.flag = 1;
            exitDfu.GetCrc();
            DataConvertor::Buf2ByteStr(reinterpret_cast<PBYTE>(&exitDfu), exitDfu.packetSize, strSendData);
        } else {
            SEBOOT_RESET st;
            st.GetCrc();
            DataConvertor::Buf2ByteStr(reinterpret_cast<PBYTE>(&st), st.packetSize, strSendData);
        }
    } else {
        strSendData = "\n";
    }
}

void BurnCtrlDfu::OnlyEraseAllSendPayload(QString &strSendData) const
{
    SEBOOT_DOWNLOAD_FLASHIMAGE st;
    st.eraseSize = 0xffffffff;
    st.fileAddr = 0;
    st.fileLen = 0;
    st.GetCrc();
    DataConvertor::Buf2ByteStr(reinterpret_cast<PBYTE>(&st), st.packetSize, strSendData);
}

void BurnCtrlDfu::WriteToPort(const char *data, int len)
{
    if (dfuMode == DfuBurnMode::HID_DFU) {
        HidDfuWriteToPort(reinterpret_cast<const uint8_t *>(data), len, false, 0);
        return;
    }
    if (dfuRoot == nullptr || mTransferSize == 0 || mTransferSize > 0x0fffffff) {
        return;
    }

    struct DfuStatus dst;
    unsigned char *buf = new unsigned char[mTransferSize];
    if (buf == nullptr) {
        return;
    }
    errno_t retMemcpy = memcpy_s(buf, mTransferSize, data, len);
    if (retMemcpy != 0) {
        printf("Error memcpy");
    }
    retValue = DfuDownload(dfuRoot->devHandle, dfuRoot->target, mTransferSize, transaction++, buf);
    if (retValue < 0) {
        WARNX("Error during download (%s)", libusb_error_name(retValue));
    }

    // 4GCat1-USB模式下，最后一条命令为reset时，界面不输出Error信息
    bool ret = this->IsResetCmd(buf, len);

    delete[] buf;
    buf = nullptr;
    do {
        retValue = DfuGetStatus(dfuRoot, dst, TIMEOUT_WRITE);
        if (retValue < 0 && !ret) {
            WARNX("Error during download get_status (%s)", libusb_error_name(retValue));
        }

        if (dst.bState == static_cast<unsigned char>(DfuState::DFU_STATE_DFU_DNLOAD_IDLE) ||
            dst.bState == static_cast<unsigned char>(DfuState::DFU_STATE_DFU_ERROR)) {
            break;
        }

        MILLI_SLEEP(dst.bwPollTimeout);
        PRINTFX("Poll timeout %i ms\n", dst.bwPollTimeout);
        if (verbose > 1) {
            PRINTFX("Poll timeout %i ms\n", dst.bwPollTimeout);
        }
    } while (true);

    if (dst.bStatus != DFU_STATUS_OK_VALUE && !ret) {
        PRINTFX(" failed!\n", "");
        PRINTFX("DFU state(%u) = %s, status(%u) = %s\n", dst.bState,
            DfuStateToString(dst.bState), dst.bStatus, DfuStatusToString(dst.bStatus));
        retValue = -1;
    }
}

void BurnCtrlDfu::HidDfuWriteToPortPartOne(int &retVal, int &overlapped, bool isLastPacket,
    unsigned short crc, OVERLAPPED &readOl) const
{
    int readCount = 0;
    while (1) {
        if (isCancel == TRUE) {
            retVal = -1;
            break;
        }
        if (readCount == NUM6000) {
            readCount = 0;
            retVal = -1;
            break;
        }
        readCount++;
        BYTE readByte[1024] = {0};
        overlapped = false;
        DWORD lpNumberOfBytesRead = 0;
        SetLastError(0);

        int retRead = ReadFile(hidDeviceHandle, readByte, 1024, &lpNumberOfBytesRead, &readOl);
        if (retRead == 0) {
            if (GetLastError() != ERROR_IO_PENDING) {
                Sleep(NUM5);
                continue;
            }
            overlapped = true;
        }

        if (overlapped == TRUE) {
            int res = WaitForSingleObject(readOl.hEvent, 10000);
            if (res != WAIT_OBJECT_0) {
                retVal = -1;
                break;
            }
            DWORD bytesRead = 0;
            /* Get the result. */
            res = GetOverlappedResult(hidDeviceHandle, &readOl, &bytesRead, FALSE);
        }
        if (isLastPacket) {
            unsigned short mCrc = 0;
            if (memcpy_s(&mCrc, sizeof (unsigned short), readByte + 1, sizeof (unsigned short)) != EOK) {
                retVal = -1;
            }
            if (mCrc != crc) {
                retVal = ERR_DFU_WRITE_FAIL;
                break;
            } else {
                break;
            }
        } else {
            break;
        }
        Sleep(NUM5);
    }

    return;
}

int BurnCtrlDfu::HidDfuWriteToPort(const uint8_t *data, int len,
    bool isLastPacket, unsigned short crc) const
{
    PBYTE byte = new BYTE[SEND_FILE_SIZE]();
    if (memset_s(byte, SEND_FILE_SIZE, 0xff, SEND_FILE_SIZE) != EOK) {
        return -1;
    }
    byte[0] = 0x00;
    if (len > SEND_FILE_SIZE - 1) {
        return -1;
    }
    if (memcpy_s(byte + 1, SEND_FILE_SIZE - 1, data, len) != EOK) {
        return -1;
    }
    LPDWORD long1 = new DWORD(0);
    OVERLAPPED writeOl;
    writeOl.hEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
    int overlapped = FALSE;
    int retwrite = WriteFile(hidDeviceHandle, byte, SEND_FILE_SIZE, long1, &writeOl);
    if (retwrite == 0) {
        int a = GetLastError();
        if (a != ERROR_IO_PENDING) {
            delete long1;
            delete[] byte;
            return -1;
        }
        overlapped = TRUE;
    }

    if (overlapped == TRUE) {
        int res = WaitForSingleObject(writeOl.hEvent, 1000);
        if (res != WAIT_OBJECT_0) {
            delete long1;
            delete[] byte;
            return -1;
        }
        DWORD bytesWritten = 0;
        /* Get the result. */
        res = GetOverlappedResult(hidDeviceHandle, &writeOl, &bytesWritten, FALSE);
    }
    OVERLAPPED readOl;
    readOl.hEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
    delete long1;
    int retVal = 0;
    HidDfuWriteToPortPartOne(retVal, overlapped, isLastPacket, crc, readOl);
    delete[] byte;
    return retVal;
}

int BurnCtrlDfu::DfuStateAction()
{
    PRINTFX("Claiming USB DFU interface...\n", "");
    retValue = libusb_claim_interface(dfuRoot->devHandle, dfuRoot->target);
    if (retValue < 0) {
        ERRX(PortableErro::EX_IOERR, "Cannot claim interface - %s", libusb_error_name(retValue));
    }

    if ((dfuRoot->flags & DFU_IF_F_ALT) != 0) {
        PRINTFX("Setting alternate interface #%d ...\n", dfuRoot->altsetting);
        retValue = libusb_set_interface_alt_setting(dfuRoot->devHandle, dfuRoot->target, dfuRoot->altsetting);
        if (retValue < 0) {
            ERRX(PortableErro::EX_IOERR, "Cannot set alternate interface: %s", libusb_error_name(retValue));
        }
    }

status_again:
    PRINTFX("Determining device status...\n", "");
    retValue = DfuGetStatus(dfuRoot, dfuStatus);
    if (retValue < 0) {
        ERRX(PortableErro::EX_IOERR, "error get_status: %s", libusb_error_name(retValue));
    }
    PRINTFX("DFU state(%u) = %s, status(%u) = %s\n", dfuStatus.bState,
        DfuStateToString(dfuStatus.bState), dfuStatus.bStatus,
        DfuStatusToString(dfuStatus.bStatus));

    MILLI_SLEEP(dfuStatus.bwPollTimeout);

    DfuState dfu = static_cast<DfuState>(dfuStatus.bState);
    switch (dfu) {
        case DfuState::DFU_STATE_APP_IDLE:
        case DfuState::DFU_STATE_APP_DETACH:
            ERRX(PortableErro::EX_PROTOCOL, "Device still in runtime mode.", "");
            break;
        case DfuState::DFU_STATE_DFU_ERROR:
            PRINTFX("Clearing status.\n", "");
            if (DfuClearStatus(dfuRoot->devHandle, dfuRoot->target) < 0) {
                ERRX(PortableErro::EX_IOERR, "error clear_status", "");
            }
            goto status_again;
            break;
        case DfuState::DFU_STATE_DFU_DNLOAD_IDLE:
        case DfuState::DFU_STATE_DFU_UPLOAD_IDLE:
            PRINTFX("Aborting previous incomplete transfer.\n", "");
            if (DfuAbort(dfuRoot->devHandle, dfuRoot->target) < 0) {
                ERRX(PortableErro::EX_IOERR, "Cannot send DFU_ABORT.", "");
            }
            goto status_again;
            break;
        case DfuState::DFU_STATE_DFU_IDLE:
        default:
            break;
    }
    return DfuStateActionSub();
}

int BurnCtrlDfu::DfuStateActionSub()
{
    if (dfuStatus.bStatus != DFU_STATUS_OK_VALUE) {
        PRINTFX("Warning: DFU status '%s'\n", DfuStatusToString(dfuStatus.bStatus));
        /* Clear our status & try again. */
        if (DfuClearStatus(dfuRoot->devHandle, dfuRoot->target) < 0) {
            ERRX(PortableErro::EX_IOERR, "USB communication error.", "");
        }
        if (DfuGetStatus(dfuRoot, dfuStatus) < 0) {
            ERRX(PortableErro::EX_IOERR, "USB communication error.", "");
        }
        if (dfuStatus.bStatus != DFU_STATUS_OK_VALUE) {
            ERRX(PortableErro::EX_PROTOCOL, "Status is not OK: %d", dfuStatus.bStatus);
        }

        MILLI_SLEEP(dfuStatus.bwPollTimeout);
    }

    PRINTFX("DFU mode device, DFU version %04x\n",
        libusb_le16_to_cpu(dfuRoot->funcDfu.bcdDFUVersion));

    if (dfuRoot->funcDfu.bcdDFUVersion == libusb_cpu_to_le16(0x11a)) {
        dfuseDevice = 1;
    }

    /* Get from device or user, warn if overridden */
    int funcDfuTransferSize = libusb_le16_to_cpu(dfuRoot->funcDfu.wTransferSize);
    if (funcDfuTransferSize != 0) {
        PRINTFX("Device returned transfer size %i\n", funcDfuTransferSize);
        mTransferSize = funcDfuTransferSize;
    } else {
        if (mTransferSize == 0) {
            ERRX(PortableErro::EX_USAGE, "Transfer size must be specified.", "");
        }
    }
    if (mTransferSize < dfuRoot->bMaxPacketSize0) {
        mTransferSize = dfuRoot->bMaxPacketSize0;
        PRINTFX("Adjusted transfer size to %i\n", mTransferSize);
    }

    return 0;
}

int BurnCtrlDfu::WaitDfuDetach()
{
    if (dfuMode == DfuBurnMode::DFU) {
        MILLI_SLEEP(MILLI_SLEEP_TIME);
    }
    matchVendor = deviceInfo.vid;
    matchProduct = deviceInfo.pid;
    ProbeDevices(ctx);

    if (dfuRoot == nullptr) {
        emit SignalSendMsg(static_cast<long>(WiteThreadMsg::WITE_THREAD_MSG_COMMON_ACTION1), 0, 0);
        ERRX(PortableErro::EX_IOERR, "Lost device after RESET?", "");
    } else if (dfuRoot->next != nullptr) {
        ERRX(PortableErro::EX_IOERR, "More than one DFU capable USB device found. "
            "Try `--list' and specify the serial number "
            "or disconnect all but one device.", "");
    }

    /* Check for DFU mode device */
    if ((dfuRoot->flags | DFU_IF_F_DFU) == 0) {
        ERRX(PortableErro::EX_PROTOCOL, "Device is not in DFU mode.", "");
    }
    PRINTFX("Opening DFU USB device...\n", "");

    /* 解决场景：当前线程已找到一个设备A，但是其他线程检测设备时占用设备A造成当前线程无法打开设备A。
       解决方案：重试20次，如果20次还是无法打开则报失败。 */
    int retryTimes = 0;
    retValue = libusb_open(dfuRoot->dev, &dfuRoot->devHandle);
    while ((retValue != 0 || !dfuRoot->devHandle) && retryTimes < MULTIPLE_USB_OPEN_RETRY_TIMES) {
        Sleep(MULTIPLE_USB_OPEN_SLEEP_TIME);
        retValue = libusb_open(dfuRoot->dev, &dfuRoot->devHandle);
        retryTimes++;
    }

    if (retValue != 0 || !dfuRoot->devHandle) {
        ERRX(PortableErro::EX_IOERR, "Cannot open device.", "");
    }
    isConnect = true;
    if (isAutoBurn) {
        emit SignalSendMsg(static_cast<long>(WiteThreadMsg::WITE_THREAD_MSG_COMMON_ACTION2), 0, 0);
    }
    return 0;
}

int BurnCtrlDfu::DfuDetach(libusb_device_handle *device,
    const unsigned short interface,
    const unsigned short timeout) const
{
    uint8_t requestType = static_cast<uint8_t>(LIBUSB_ENDPOINT_OUT) | static_cast<uint8_t>(LIBUSB_REQUEST_TYPE_CLASS)
            | static_cast<uint8_t>(LIBUSB_RECIPIENT_INTERFACE);
    return libusb_control_transfer(device,
                                   /* bmRequestType */
                                   requestType,
                                   DFU_DETACH, /* bRequest */
                                   timeout, /* wValue */
                                   interface, /* wIndex */
                                   nullptr, /* Data */
                                   0, /* wLength */
                                   dfuTimeout);
}

int BurnCtrlDfu::DfuDownload(libusb_device_handle *device,
    const unsigned short interface,
    const unsigned short length,
    const unsigned short wValue,
    PBYTE data) const
{
    int status;

    uint8_t requestType = static_cast<uint8_t>(LIBUSB_ENDPOINT_OUT) | static_cast<uint8_t>(LIBUSB_REQUEST_TYPE_CLASS)
            | static_cast<uint8_t>(LIBUSB_RECIPIENT_INTERFACE);
    status = libusb_control_transfer(device,
                                     /* bmRequestType */
                                     requestType,
                                     DFU_DNLOAD, /* bRequest */
                                     wValue, /* wValue */
                                     interface, /* wIndex */
                                     data, /* Data */
                                     length, /* wLength */
                                     dfuTimeout);
    return status;
}

int BurnCtrlDfu::DfuGetStatus(struct DfuIf *&dif, DfuStatus &status, int timeout) const
{
    unsigned char buffer[6];
    int result;

    /* Initialize the status data structure */
    status.bStatus = DFU_STATUS_ERROR_UNKNOWN;
    status.bwPollTimeout = 0;
    status.bState = STATE_DFU_ERROR;
    status.iString = 0;

    uint8_t requestType = static_cast<uint8_t>(LIBUSB_ENDPOINT_IN) | static_cast<uint8_t>(LIBUSB_REQUEST_TYPE_CLASS)
            | static_cast<uint8_t>(LIBUSB_RECIPIENT_INTERFACE);
    result = libusb_control_transfer(dif->devHandle,
                                     /* bmRequestType */
                                     requestType,
                                     DFU_GETSTATUS, /* bRequest */
                                     0, /* wValue */
                                     dif->target, /* wIndex */
                                     buffer, /* Data */
                                     WLENGTH_SIX, /* wLength */
                                     timeout);
    if (result == WLENGTH_SIX) {
        status.bStatus = buffer[0];
        if ((dif->quirks & QUIRK_POLLTIMEOUT) != 0) {
            status.bwPollTimeout = DEFAULT_POLLTIMEOUT;
        } else {
            status.bwPollTimeout = ((static_cast<unsigned int>(0xff) & buffer[THREE_STOP]) << BURNCTRL_DFU_NUM_16) |
            ((0xff & buffer[TWO_STOP]) << BURNCTRL_DFU_NUM_8) |
            (0xff & buffer[1]);
        }
        status.bState = buffer[FOUR_STOP];
        status.iString = buffer[FIVE_STOP];
    }

    return result;
}

int BurnCtrlDfu::DfuClearStatus(libusb_device_handle *device,
    const unsigned short interface) const
{
    uint8_t requestType = static_cast<uint8_t>(LIBUSB_ENDPOINT_OUT) | static_cast<uint8_t>(LIBUSB_REQUEST_TYPE_CLASS)
            | static_cast<uint8_t>(LIBUSB_RECIPIENT_INTERFACE);
    return libusb_control_transfer(device,
                                   /* bmRequestType */
                                   requestType,
                                   DFU_CLRSTATUS, /* bRequest */
                                   0, /* wValue */
                                   interface, /* wIndex */
                                   nullptr, /* Data */
                                   0, /* wLength */
                                   dfuTimeout);
}

int BurnCtrlDfu::DfuGetState(libusb_device_handle *device,
    const unsigned short interface) const
{
    int result;
    unsigned char buffer[1];

    uint8_t requestType = static_cast<uint8_t>(LIBUSB_ENDPOINT_IN) | static_cast<uint8_t>(LIBUSB_REQUEST_TYPE_CLASS)
            | static_cast<uint8_t>(LIBUSB_RECIPIENT_INTERFACE);
    result = libusb_control_transfer(device,
                                     /* bmRequestType */
                                     requestType,
                                     DFU_GETSTATE, /* bRequest */
                                     0, /* wValue */
                                     interface, /* wIndex */
                                     buffer, /* Data */
                                     1, /* wLength */
                                     dfuTimeout);
    if (result < 1) {
        return -1;
    }
    return buffer[0];
}

int BurnCtrlDfu::DfuAbort(libusb_device_handle *device,
    const unsigned short interface) const
{
    uint8_t requestType = static_cast<uint8_t>(LIBUSB_ENDPOINT_OUT) | static_cast<uint8_t>(LIBUSB_REQUEST_TYPE_CLASS)
            | static_cast<uint8_t>(LIBUSB_RECIPIENT_INTERFACE);
    return libusb_control_transfer(device,
                                   /* bmRequestType */
                                   requestType,
                                   DFU_ABORT, /* bRequest */
                                   0, /* wValue */
                                   interface, /* wIndex */
                                   nullptr, /* Data */
                                   0, /* wLength */
                                   dfuTimeout);
}

const char* BurnCtrlDfu::DfuStateToString(int state) const
{
    const char *message;

    switch (state) {
        case STATE_APP_IDLE:
            message = "appIDLE";
            break;
        case STATE_APP_DETACH:
            message = "appDETACH";
            break;
        case STATE_DFU_IDLE:
            message = "dfuIDLE";
            break;
        case STATE_DFU_DOWNLOAD_SYNC:
            message = "dfuDNLOAD-SYNC";
            break;
        case STATE_DFU_DOWNLOAD_BUSY:
            message = "dfuDNBUSY";
            break;
        case STATE_DFU_DOWNLOAD_IDLE:
            message = "dfuDNLOAD-IDLE";
            break;
        case STATE_DFU_MANIFEST_SYNC:
            message = "dfuMANIFEST-SYNC";
            break;
        case STATE_DFU_MANIFEST:
            message = "dfuMANIFEST";
            break;
        case STATE_DFU_MANIFEST_WAIT_RESET:
            message = "dfuMANIFEST-WAIT-RESET";
            break;
        case STATE_DFU_UPLOAD_IDLE:
            message = "dfuUPLOAD-IDLE";
            break;
        case STATE_DFU_ERROR:
            message = "dfuERROR";
            break;
        default:
            message = nullptr;
            break;
    }

    return message;
}

const char *BurnCtrlDfu::DfuStatusToString(int status) const
{
    if (status > DFU_STATUS_ERR_STALLEDPKT) {
        return "INVALID";
    }
    return DFU_STATUS_NAME[status];
}

int BurnCtrlDfu::ProbePrefix(DfuFile &mFile) const
{
    uint8_t *prefix = mFile.firmware;

    if (mFile.size.total < LMDFU_PREFIX_LENGTH) {
        return 1;
    }
    if ((prefix[0] == 0x01) && (prefix[1] == 0x00)) {
        uint32_t payloadLength = (static_cast<unsigned int>(prefix[BURNCTRL_DFU_NUM_7]) << BURNCTRL_DFU_NUM_24) |
                                  (static_cast<unsigned int>(prefix[BURNCTRL_DFU_NUM_6]) << BURNCTRL_DFU_NUM_16) |
                                  (static_cast<unsigned int>(prefix[BURNCTRL_DFU_NUM_5]) << BURNCTRL_DFU_NUM_8) |
                                  prefix[BURNCTRL_DFU_NUM_4];
        uint32_t expectedPayloadLength = static_cast<uint32_t>(mFile.size.total) -
                LMDFU_PREFIX_LENGTH - mFile.size.suffix;
        if (payloadLength != expectedPayloadLength) {
            return 1;
        }
        mFile.prefixType = static_cast<uint32_t>(PrefixType::LMDFU_PREFIX);
        mFile.size.prefix = LMDFU_PREFIX_LENGTH;
        mFile.lmdfuAddress = LMDFU_ADDRESS_LENGTH *
                              ((prefix[BURNCTRL_DFU_NUM_3] << BURNCTRL_DFU_NUM_8) | prefix[BURNCTRL_DFU_NUM_2]);
    } else if (((prefix[0] & BURNCTRL_DFU_NUM_3F) == 0x1a) &&
               ((prefix[1] & BURNCTRL_DFU_NUM_3F) == BURNCTRL_DFU_NUM_3F)) {
        mFile.prefixType = static_cast<uint32_t>(PrefixType::LPCDFU_UNENCRYPTED_PREFIX);
        mFile.size.prefix = LPCDFU_PREFIX_LENGTH;
    }

    if (mFile.size.prefix + mFile.size.suffix > mFile.size.total) {
        return 1;
    }
    return 0;
}

void BurnCtrlDfu::SetProgressRange()
{
    int totalFileSize = 0;
    for (int i = 0; i < infoArray.size(); i++) {
        if (isLoaderDfu && infoArray[i].type == IMAGE_TYPE::IMAGE_TYPE_LOADER) {
            continue;
        }

        totalFileSize += DataConvertor::Str2U32DecHex(infoArray[i].fileSize);
    }
    BurnCtrl::SetProgressRange(0, SHRT_MAX);
    burnFileSize = 0;
    UpdateProgress(0, totalFileSize);
}

void BurnCtrlDfu::UpdateProgress(UINT progress)
{
    BurnCtrl::SetProgressValue(progress);
    NotifyState(static_cast<int>(fsm), progress);
}

void BurnCtrlDfu::UpdateProgress(U32 fileSize, UINT totalFileSize)
{
    if (totalFileSize == 0) {
        for (int i = 0; i < infoArray.size(); i++) {
            if (!Global::IsUSBType(chipType) && isLoaderDfu
                && infoArray[i].type != IMAGE_TYPE::IMAGE_TYPE_LOADER) {
                continue;
            }
            totalFileSize += DataConvertor::Str2U32DecHex(infoArray[i].fileSize);
        }
    }
    UINT progress = 0;
    burnFileSize += fileSize;
    if (burnFileSize == static_cast<int>(totalFileSize)) {
        progress = SHRT_MAX;
        burnFileSize = 0;
    } else if (burnFileSize == 0 || totalFileSize == 0) {
        progress = 0;
    } else {
        progress = static_cast<float>((static_cast<float>(burnFileSize) / totalFileSize)) * SHRT_MAX;
    }
    UpdateProgress(progress);
}

void BurnCtrlDfu::DfuProgressBar(const char *desc, unsigned long long curr, unsigned long long max)
{
    static char buf[PROGRESS_BAR_WIDTH + 1];
    static unsigned long long lastProgress = -1;
    static time_t lastTime;
    time_t currTime = time(nullptr);
    unsigned long long progress;
    unsigned long long mX;

    if (max < curr) {
        max = curr + 1;
    }
    if (max == 0) {
        max = 1;
    }
    progress = (PROGRESS_BAR_WIDTH * curr) / max;
    if (progress > PROGRESS_BAR_WIDTH) {
        progress = PROGRESS_BAR_WIDTH;
    }
    if (progress == lastProgress &&
        currTime == lastTime) {
        return;
    }
    lastProgress = progress;
    lastTime = currTime;

    for (mX = 0; mX != PROGRESS_BAR_WIDTH; mX++) {
        if (mX < progress) {
            buf[mX] = '=';
        } else {
            buf[mX] = ' ';
        }
    }

    PRINTFX("\r%s\t[%s] %3llu%% %12llu bytes", desc, buf, (100ULL * curr) / max, curr);
    if (progress == PROGRESS_BAR_WIDTH) {
        PRINTFX("\n%s done.\n", desc);
    }
}

int BurnCtrlDfu::DfuLoadFile(DfuFile &mFile, enum SuffixReq checkSuffix, enum PrefixReq checkPrefix,
                             unsigned int index, unsigned int fileSize)
{
    mFile.size.prefix = 0;
    mFile.size.suffix = 0;

    mFile.bcdDFU = 0;
    mFile.idVendor = 0xffff;
    mFile.idProduct = 0xffff;
    mFile.bcdDevice = 0xffff;
    mFile.lmdfuAddress = 0;
    if (mFile.firmware != nullptr) {
        free(mFile.firmware);
        mFile.firmware = nullptr;
    }

    if (strcmp(mFile.name, "-") == 0) {
        size_t readBytes;

#ifdef WIN32
        _setmode(_fileno(stdin), _O_BINARY);
#endif
        mFile.firmware = (uint8_t*)malloc(STDIN_CHUNK_SIZE);
        if (mFile.firmware == nullptr) {
            ERR(PortableErro::EX_SOFTWARE, "Could not allocate firmware buffer.", "");
        }

        readBytes = fread(mFile.firmware, 1, STDIN_CHUNK_SIZE, stdin);
        mFile.size.total = readBytes;
        while (readBytes == STDIN_CHUNK_SIZE) {
            uint8_t *demp = mFile.firmware;
            mFile.firmware = (uint8_t*)malloc(mFile.size.total + STDIN_CHUNK_SIZE);
            free(demp);
            if (!mFile.firmware) {
                ERR(PortableErro::EX_SOFTWARE, "Could not allocate firmware buffer.", "");
            }
            readBytes = fread(mFile.firmware + mFile.size.total, 1, STDIN_CHUNK_SIZE, stdin);
            mFile.size.total += readBytes;
        }
        if (verbose != 0) {
            PRINTFX("Read %lli bytes from stdin.\n", static_cast<long long>(mFile.size.total));
        }
        /* Never require suffix when reading from stdin */
        checkSuffix = SuffixReq::MAYBE_SUFFIX;
    } else {
        PartOfDfuLoadFile(mFile, index, fileSize);
    }

    return DfuLoadFileSub(mFile, checkSuffix, checkPrefix);
}

int BurnCtrlDfu::PartOfDfuLoadFile(DfuFile &mFile, unsigned int index, unsigned int fileSize)
{
    Offt readTotal = 0;
    QString cleanPath = QDir::cleanPath(mFile.name);
    if (cleanPath.isEmpty()) {
        return PortableErro::EX_SOFTWARE;
    }
    QFile fileInfo(cleanPath);
    if (!fileInfo.open(QIODevice::ReadOnly)) {
        ERR(PortableErro::EX_NOINPUT, "Could not open file %s for reading.", mFile.name);
    }
    uint64_t offset = fileInfo.size();
    if (offset < 0) {
        ERR(PortableErro::EX_SOFTWARE, "File size too big.", "");
    }

    mFile.size.total = offset;
    if (fileSize != 0) {
        mFile.size.total = fileSize;
    }

    if (static_cast<unsigned long long>(mFile.size.total) > SIZE_MAX) {
        ERR(PortableErro::EX_SOFTWARE, "File too large for memory allocation on this platform.", "");
    }
    mFile.firmware = (uint8_t*)malloc(mFile.size.total);
    if (mFile.firmware == nullptr) {
        ERR(PortableErro::EX_SOFTWARE, "Could not allocate firmware buffer.", "");
    }

    if (index != 0) {
        fileInfo.seek(index);
    }
    while (readTotal < mFile.size.total) {
        Offt toRead = mFile.size.total - readTotal;
        if (toRead > 0x7ffff000) {
            toRead = 0x7ffff000;
        }
        size_t readCount = fileInfo.read(reinterpret_cast<CHAR *>(mFile.firmware + readTotal), toRead);
        if (readCount == 0) {
            break;
        }
        if (readCount == static_cast<size_t>(-1) && errno != EINTR) {
            break;
        }
        readTotal += readCount;
    }
    if (readTotal != mFile.size.total) {
        ERR(PortableErro::EX_IOERR, "Could only read %lld of %lld bytes from %s.",
            static_cast<long long>(readTotal), static_cast<long long>(mFile.size.total), mFile.name);
    }
    fileInfo.close();
}

int BurnCtrlDfu::DfuLoadFileSub(DfuFile &mFile, const enum SuffixReq &checkSuffix, const enum PrefixReq &checkPrefix)
{
    uint32_t crc = 0xffffffff;
    const uint8_t *dfusuffix;
    int missingSuffix = 0;

    if (mFile.firmware == nullptr) {
        return PortableErro::EX_DATAERR;
    }
    if (mFile.size.total < DFU_SUFFIX_LENGTH) {
        missingSuffix = 1;
    } else {
        dfusuffix = mFile.firmware + mFile.size.total - DFU_SUFFIX_LENGTH;
        for (int i = 0; i < mFile.size.total - DFU_DELETE_LENGTH; i++) {
            crc = ChannelClass::Channel::Crc32Byte(crc, mFile.firmware[i]);
        }
    }
    if (missingSuffix != 0) {
        if (checkSuffix == SuffixReq::NEEDS_SUFFIX) {
            ERRX(PortableErro::EX_DATAERR, "Valid DFU suffix needed.", "");
        } else if (checkSuffix == SuffixReq::MAYBE_SUFFIX) {
            WARNX("A valid DFU suffix will be required in a future dfu-util release.", "");
        }
    } else {
        if (checkSuffix == SuffixReq::NO_SUFFIX) {
            ERRX(PortableErro::EX_DATAERR, "Remove existing DFU suffix before adding a new one.\n", "");
        }
    }
    if ((ProbePrefix(mFile) != 0 || mFile.size.prefix == 0) && checkPrefix == PrefixReq::NEEDS_PREFIX) {
        ERRX(PortableErro::EX_DATAERR, "Valid DFU prefix needed.", "");
    }
    if (mFile.size.prefix != 0 && checkPrefix == PrefixReq::NO_PREFIX) {
        ERRX(PortableErro::EX_DATAERR, "A prefix already exists. Delete it first.", "");
    }
    if (mFile.size.prefix != 0 && verbose != 0) {
        uint8_t *data = mFile.firmware;
        if (mFile.prefixType == static_cast<uint32_t>(PrefixType::LMDFU_PREFIX)) {
            PRINTFX("Possible TI Stellaris DFU prefix with the following properties:\n"
                "Address:        0x%08x\nPayload length: %d\n",
                mFile.lmdfuAddress, data[FOUR_STOP] | (data[BURNCTRL_DFU_NUM_5] << BURNCTRL_DFU_NUM_8) |
                (static_cast<unsigned int>(data[SIX_STOP]) << BURNCTRL_DFU_NUM_16) |
                (static_cast<unsigned int>(data[BURNCTRL_DFU_NUM_7]) << BURNCTRL_DFU_NUM_24));
        } else if (mFile.prefixType == static_cast<uint32_t>(PrefixType::LPCDFU_UNENCRYPTED_PREFIX)) {
            PRINTFX("Possible unencrypted NXP LPC DFU prefix with the following properties:\n"
                "Payload length: %d kiByte\n", data[TWO_STOP] >> 1 | (data[BURNCTRL_DFU_NUM_3] << BURNCTRL_DFU_NUM_7));
        } else {
            ERRX(PortableErro::EX_DATAERR, "Unknown DFU prefix type.", "");
        }
    }
    return 0;
}

int BurnCtrlDfu::DfuloadDoDnloadHidDfu(DfuFile &mFile)
{
    Offt bytesSent = 0;
    PBYTE buf = mFile.firmware;
    Offt expectedSize = mFile.size.total - mFile.size.suffix;
    unsigned short crc = ChannelClass::Channel::SopCrc16(buf, expectedSize);
    while (bytesSent < expectedSize) {
        bool isLastPacket = false;
        if (isShutDown) {
            return ERR_SOP_CPAB_NOT_CONNECT_UE;
        }
        int chunkSize = 0;
        int xferSize = 1024;
        Offt bytesLeft = expectedSize - bytesSent;
        if (bytesLeft <= xferSize) {
            isLastPacket = true;
            chunkSize = static_cast<int>(bytesLeft);
        } else {
            chunkSize = xferSize;
        }
        int ret = HidDfuWriteToPort(buf, chunkSize, isLastPacket, crc);
        if (ret != 0) {
            return ret;
        }
        bytesSent += chunkSize;
        buf += chunkSize;
        DfuProgressBar("Download", bytesSent, bytesSent + bytesLeft);
        UpdateProgress(static_cast<U32>(chunkSize));
    }
    PRINTFX("Done!\n", "");
    return 0;
}

int BurnCtrlDfu::DfuloadDoDnload(struct DfuIf *&dif, int xferSize, DfuFile &mFile)
{
    Offt bytesSent = 0;
    unsigned char *buf;
    struct DfuStatus dfuloadStatus;
    int ret = 0;
    PRINTFX("Copying data from PC to DFU device.\n", "");
    buf = mFile.firmware;
    Offt expectedSize = mFile.size.total - mFile.size.suffix;
    DfuProgressBar("Download", 0, 1);
    UpdateProgress(static_cast<U32>(0));
    if (dfuMode == DfuBurnMode::HID_DFU) {
        return DfuloadDoDnloadHidDfu(mFile);
    }
    while (bytesSent < expectedSize) {
        if (isShutDown) {
            return ERR_SOP_CPAB_NOT_CONNECT_UE;
        }
        int chunkSize = 0;

        Offt bytesLeft = expectedSize - bytesSent;
        if (bytesLeft < xferSize) {
            chunkSize = static_cast<int>(bytesLeft);
        } else {
            chunkSize = xferSize;
        }
        ret = DfuDownload(dif->devHandle, dif->target,
            chunkSize, transaction++, chunkSize != 0 ? buf : nullptr);
        if (ret < 0) {
            WARNX("Error during download (%s)", libusb_error_name(ret));
            return ret;
        }
        bytesSent += chunkSize;
        buf += chunkSize;

        do {
            if (isShutDown) {
                return ERR_SOP_CPAB_NOT_CONNECT_UE;
            }

            ret = DfuGetStatus(dif, dfuloadStatus);
            if (ret < 0) {
                WARNX("Error during download get_status (%s)", libusb_error_name(ret));
                return 0;
            }
            if (dfuloadStatus.bState == static_cast<unsigned char>(DfuState::DFU_STATE_DFU_DNLOAD_IDLE) ||
                dfuloadStatus.bState == static_cast<unsigned char>(DfuState::DFU_STATE_DFU_ERROR)) {
                break;
            }

            /* Wait while device executes flashing */
            MILLI_SLEEP(dfuloadStatus.bwPollTimeout);
            if (verbose > 1) {
                PRINTFX("Poll timeout %i ms\n", dfuloadStatus.bwPollTimeout);
            }
        } while (true);

        if (dfuloadStatus.bStatus != DFU_STATUS_OK_VALUE) {
            PRINTFX(" failed!\n", "");
            PRINTFX("DFU state(%u) = %s, status(%u) = %s\n", dfuloadStatus.bState,
                DfuStateToString(dfuloadStatus.bState), dfuloadStatus.bStatus,
                DfuStatusToString(dfuloadStatus.bStatus));
            ret = -1;
            return ret;
        }
        DfuProgressBar("Download", bytesSent, bytesSent + bytesLeft);
        UpdateProgress(static_cast<U32>(chunkSize));
    }
    ret = DfuDownload(dif->devHandle, dif->target, 0, transaction, nullptr);
    if (ret < 0) {
        ERRX(PortableErro::EX_IOERR, "Error sending completion packet (%s)",
            libusb_error_name(ret));
        return ret;
    }

    DfuProgressBar("Download", bytesSent, bytesSent);

    if (verbose != 0) {
        PRINTFX("Sent a total of %lli bytes.\n", static_cast<long long>(bytesSent));
    }

get_status:
    ret = DfuGetStatus(dif, dfuloadStatus);
    if ((!isWriteLoader) && (ret < 0)) {
        WARNX("Unable to read DFU status after completion (%s).",
            libusb_error_name(ret));
        return ret;
    }
    PRINTFX("DFU state(%u) = %s, status(%u) = %s\n", dfuloadStatus.bState,
        DfuStateToString(dfuloadStatus.bState), dfuloadStatus.bStatus,
        DfuStatusToString(dfuloadStatus.bStatus));

    MILLI_SLEEP(dfuloadStatus.bwPollTimeout);

    DfuState dfu = static_cast<DfuState>(dfuloadStatus.bState);
    switch (dfu) {
        case DfuState::DFU_STATE_DFU_MANIFEST_SYNC:
        case DfuState::DFU_STATE_DFU_MANIFEST:
            /* some devices (e.g. TAS1020b) need some time before we
             * can obtain the status */
            MILLI_SLEEP(MILLI_SLEEP_TIME);
            goto get_status;
        case DfuState::DFU_STATE_DFU_MANIFEST_WAIT_RST:
            PRINTFX("Resetting USB to switch back to runtime mode.\n", "");
            ret = libusb_reset_device(dif->devHandle);
            if (ret < 0 && ret != LIBUSB_ERROR_NOT_FOUND) {
                PRINTFX("Error resetting after download (%s)\n",
                    libusb_error_name(ret));
            }
            break;
        case DfuState::DFU_STATE_DFU_IDLE:
            break;
        default:
            break;
    }
    if (dfuMode == DfuBurnMode::DFU) {
        PRINTFX(IDS_STRING_32879.toUtf8().data(), "");
        PRINTFX("Upgrading, do not power off! Please wait...\n", "");
        PRINTFX(IDS_STRING_32879.toUtf8().data(), "");
        bool retIsCompleted = false;
        unsigned int count = 0;
        while (!retIsCompleted) {
            if (count >= 100) { // 循环100次
                PRINTFX("Upgrade failed.\n", "");
                break;
            }
            retIsCompleted = IsUpgradeCompleted();
            Sleep(500); // 每次查询时间间隔500ms
            count++;
        }
    }
    PRINTFX("Done.\n", "");
    return 0;
}

int BurnCtrlDfu::FindDescriptor(const uint8_t *descList, int listLen,
                                uint8_t descType, PVOID resBuf, int resSize)
{
    int p = 0;

    if (listLen < LIST_LEN_MINI_VALUE) {
        return (-1);
    }

    while (p + 1 < listLen) {
        int desclen = static_cast<int>(descList[p]);
        if (desclen == 0) {
            WARNX("Invalid descriptor list.", "");
            return -1;
        }
        if (descList[p + 1] == descType) {
            if (desclen > resSize) {
                desclen = resSize;
            }
            if (p + desclen > listLen) {
                desclen = listLen - p;
            }
            if (memcpy_s(resBuf, resSize, &descList[p], desclen) != EOK) {
                WARNX("memcpy error.", "");
            }
            return desclen;
        }
        p += static_cast<int>(descList[p]);
    }
    return -1;
}

int BurnCtrlDfu::GetUtf8StringDescriptor(libusb_device_handle *devh, uint8_t descIndex, uint8_t *data, int length)
{
    unsigned char tbuf[255];
    uint16_t langid;
    int r;
    int outlen;
    r = libusb_get_string_descriptor(devh, 0, 0, tbuf, sizeof(tbuf));
    if (r < 0) {
        WARNX("Failed to retrieve language identifiers.", "");
        return r;
    }
    if (r < VALUE_RANGE_FOUR || tbuf[0] < VALUE_RANGE_FOUR || tbuf[1] != LIBUSB_DT_STRING) {
        WARNX("Broken LANGID string descriptor.", "");
        return -1;
    }
    langid = tbuf[TWO_STOP] | (tbuf[BURNCTRL_DFU_NUM_3] << BURNCTRL_DFU_NUM_8);

    r = libusb_get_string_descriptor(devh, descIndex, langid, tbuf,
        sizeof(tbuf));
    if (r < 0) {
        WARNX("Failed to retrieve string descriptor %d.", descIndex);
        return r;
    }
    if (r < VALUE_RANGE_TWO || tbuf[0] < VALUE_RANGE_TWO) {
        WARNX("String descriptor %d too short.", descIndex);
        return -1;
    }
    if (tbuf[1] != LIBUSB_DT_STRING) { /* sanity check */
        WARNX("Malformed string descriptor %d, type = 0x%02x", descIndex, tbuf[1]);
        return -1;
    }
    if (tbuf[0] > r) { /* if short read, */
        WARNX("Patching string descriptor %d length (was %d, received %d)", descIndex, tbuf[0], r);
        tbuf[0] = r; /* fix up descriptor length */
    }

    outlen = tbuf[0] - OUTLEN_DELETE_LENGTH;
    if (length < outlen) {
        outlen = length;
    }

    errno_t retMemcpy = memcpy_s(data, length, tbuf + TBUF_ADD_LENGTH, outlen);
    if (retMemcpy != 0) {
        qDebug() << "Error memcpy";
    }
    return outlen;
}

int BurnCtrlDfu::GetStringDescriptorAscii(libusb_device_handle *devh, uint8_t descIndex,
                                          uint8_t *data, int length)
{
    unsigned char buf[255];
    int r;
    int di;
    int si;

    r = GetUtf8StringDescriptor(devh, descIndex, buf, sizeof(buf));
    if (r < 0) {
        return r;
    }
    for (di = 0, si = 0; si + 1 < r && di < length; si += NUMBER_ADD) {
        /* high byte of unicode char */
        if (buf[si + 1] != 0) {
            data[di++] = '?';
        } else {
            data[di++] = buf[si];
        }
    }
    data[di] = '\0';
    return di;
}

int BurnCtrlDfu::ProbeConfiguration(libusb_device *dev, libusb_device_descriptor &desc)
{
    struct UsbDfu::UsbDfuFuncDescriptor funcDfu;
    libusb_device_handle *devh;
    struct DfuIf *pdfu;
    struct libusb_config_descriptor *cfg;
    const struct libusb_interface_descriptor *intf;
    const struct libusb_interface *uif;
    char altName[MAX_DESC_STR_LEN + 1] = {0};
    char serialName[MAX_DESC_STR_LEN + 1] = {0};
    int cfgIdx;
    int intfIdx;
    int altIdx;
    int ret = 0;
    int hasDfu;

    for (cfgIdx = 0; cfgIdx != desc.bNumConfigurations; cfgIdx++) {
        errno_t retMemset = memset_s(&funcDfu, sizeof(funcDfu), 0, sizeof(funcDfu));
        if (retMemset != 0) {
            qDebug() << "Error memset";
            return -1;
        }

        hasDfu = 0;

        ret = libusb_get_config_descriptor(dev, cfgIdx, &cfg);
        if (ret != 0) {
            return ret;
        }
        if (!cfg) {
            return ret;
        }

        ret = FindDescriptor(cfg->extra, cfg->extra_length, USB_DT_DFU, &funcDfu, sizeof(funcDfu));
        if (ret > -1) {
            goto found_dfu;
        }

        for (intfIdx = 0; intfIdx < cfg->bNumInterfaces; intfIdx++) {
            uif = &cfg->interface[intfIdx];
            if (!uif) {
                break;
            }

            for (altIdx = 0; altIdx < cfg->interface[intfIdx].num_altsetting; altIdx++) {
                intf = &uif->altsetting[altIdx];
                if (intf->bInterfaceClass != 0xfe || intf->bInterfaceSubClass != 1) {
                    continue;
                }

                ret = FindDescriptor(intf->extra, intf->extra_length, USB_DT_DFU,
                    &funcDfu, sizeof(funcDfu));
                if (ret > -1) {
                    goto found_dfu;
                }

                hasDfu = 1;
            }
        }
        if (hasDfu != 0) {
            if (libusb_open(dev, &devh) == 0) {
                ret = libusb_get_descriptor(devh, USB_DT_DFU, 0,
                    reinterpret_cast<PBYTE>(&funcDfu), sizeof(funcDfu));
                libusb_close(devh);
                if (ret > -1) {
                    goto found_dfu;
                }
            }
            WARNX("Device has DFU interface, but has no DFU functional descriptor.", "");
            /* fake version 1.0 */
            funcDfu.bLength = FUNC_DFU_BLENGTH;
            funcDfu.bcdDFUVersion = libusb_cpu_to_le16(0x0100);
            goto found_dfu;
        }
        libusb_free_config_descriptor(cfg);
        continue;

    found_dfu:
        if (funcDfu.bLength == FUNC_DFU_BLENGTH) {
            PRINTFX("Deducing device DFU version from functional descriptor length.\n", "");
            funcDfu.bcdDFUVersion = libusb_cpu_to_le16(0x0100);
        } else if (funcDfu.bLength < FUNC_DFU_BLENGTH_RANGE_NINE) {
            PRINTFX("Error obtaining DFU functional descriptor.\n", "");
            PRINTFX("Report this as a bug.\n", "");
            PRINTFX("Warning: Assuming DFU version 1.0.\n", "");
            funcDfu.bcdDFUVersion = libusb_cpu_to_le16(0x0100);
            PRINTFX("Warning: Transfer size cannot be detected.\n", "");
            funcDfu.wTransferSize = 0;
        }

        for (intfIdx = 0; intfIdx < cfg->bNumInterfaces; intfIdx++) {
            int multipleAlt;

            uif = &cfg->interface[intfIdx];
            if (!uif) {
                break;
            }

            multipleAlt = uif->num_altsetting > 0;

            for (altIdx = 0; altIdx < uif->num_altsetting; altIdx++) {
                int dfuMode;
                uint16_t quirks;

                quirks = GetQuirks(desc.idVendor, desc.idProduct, desc.bcdDevice);
                intf = &uif->altsetting[altIdx];
                if (intf->bInterfaceClass != 0xfe || intf->bInterfaceSubClass != 1) {
                    continue;
                }

                dfuMode = (intf->bInterfaceProtocol == BINTERFACEPROTOCOL_VALUE_TWO);
                ProbeConfigurationSub1(intf, dfuMode, funcDfu, desc, cfg);

                if (dfuMode != 0) {
                } else {
                    if ((matchVendor >= 0 && matchVendor != desc.idVendor) ||
                        (matchProduct >= 0 && matchProduct != desc.idProduct)) {
                        continue;
                    }
                }

                ret = libusb_open(dev, &devh);
                if (ret != 0) {
                    WARNX("Cannot open DFU device %04x:%04x found on devnum %i (%s).",
                          desc.idVendor, desc.idProduct, libusb_get_device_address(dev),
                          libusb_error_name(ret));
                    break;
                }
                if (intf->iInterface != 0) {
                    ret = GetStringDescriptorAscii(devh, intf->iInterface,
                                                   reinterpret_cast<uint8_t*>(altName), MAX_DESC_STR_LEN);
                } else {
                    ret = -1;
                }
                if (ret < 1) {
                    errno_t retStrcpy = strcpy_s(altName, sizeof(altName), "UNKNOWN");
                    if (retStrcpy != 0) {
                        qDebug() << "Error Strcpy";
                    }
                }
                ProbeConfigurationSub2(desc, quirks, ret, devh, serialName);
                if (ret < 1) {
                    errno_t retStrcpy = strcpy_s(serialName, sizeof(serialName), "UNKNOWN");
                    if (retStrcpy != 0) {
                        qDebug() << "Error Strcpy";
                    }
                }
                libusb_close(devh);

                pdfu = (DfuIf *)malloc(sizeof(DfuIf));
                if (pdfu == nullptr) {
                    return 0;
                }
                errno_t retMSet = memset_s(pdfu, sizeof(*pdfu), 0, sizeof(*pdfu));
                if (retMSet != 0) {
                    qDebug() << "Error memset";
                }

                pdfu->funcDfu = funcDfu;
                pdfu->dev = libusb_ref_device(dev);
                pdfu->quirks = quirks;
                pdfu->vendor = desc.idVendor;
                pdfu->product = desc.idProduct;
                pdfu->bcdDevice = desc.bcdDevice;
                pdfu->configuration = cfg->bConfigurationValue;
                pdfu->target = intf->bInterfaceNumber;
                pdfu->altsetting = intf->bAlternateSetting;
                pdfu->devnum = libusb_get_device_address(dev);
                pdfu->busnum = libusb_get_bus_number(dev);
                pdfu->altName = strdup(altName);
                if (pdfu->altName == nullptr) {
                    ERRX(PortableErro::EX_SOFTWARE, "Out of memory.", "");
                }
                pdfu->serialName = strdup(serialName);
                if (pdfu->serialName == nullptr) {
                    ERRX(PortableErro::EX_SOFTWARE, "Out of memory.", "");
                }
                if (dfuMode != 0) {
                    pdfu->flags |= DFU_IF_F_DFU;
                }
                if (multipleAlt != 0) {
                    pdfu->flags |= DFU_IF_F_ALT;
                }
                if ((pdfu->quirks & QUIRK_FORCE_DFU11) != 0) {
                    pdfu->funcDfu.bcdDFUVersion =
                        libusb_cpu_to_le16(0x0110);
                }
                pdfu->bMaxPacketSize0 = desc.bMaxPacketSize0;

                QString mSerialName = pdfu->serialName;
                if ((QString::compare(mSerialName, "UNKNOWN") == 0) || (mSerialName.isEmpty())) {
                    qDebug() << "Currrent Thread[" << QThread::currentThreadId() <<
                                "] USB Serial Name is UNKNOWN or empty";
                    libusb_free_config_descriptor(cfg);
                    return USB_SERIAL_UNKNOWN;
                }

                if (isOneTimeBurn != 0 &&
                    (!deviceInfo.serialName.isEmpty() &&
                     mSerialName.compare(deviceInfo.serialName, Qt::CaseInsensitive) != 0)) {
                    WARNX("Cannot find DFU device. Target serialName: %s actual serial: %s.",
                          deviceInfo.serialName.toUtf8().data(), mSerialName.toUtf8().data());
                    return -1;
                }

                // 加锁，防止每一个线程进入抢占同一个USB设备
                // 找到其他线程占用的USB设备并且USB序列号和当前线程不一样，则退出继续查找
                Global::mMutex.lock();
                if (Global::mUsbSerialLists.contains(mSerialName) && (m_serialName.compare(mSerialName) != 0)) {
                    libusb_free_config_descriptor(cfg);
                    Global::mMutex.unlock();
                    return OTHER_THREAD_OCCUPY;
                }
                Global::mMutex.unlock();
                // 找到其他线程没占用的USB设备，需要给dfuRoot赋值并在Global::m_usbSerialLists插入新的序列号
                if (m_serialName.isEmpty()) {
                    /* queue into list */
                    pdfu->next = dfuRoot;
                    dfuRoot = pdfu;

                    m_serialName = mSerialName;
                    Global::mMutex.lock();
                    if (!Global::mUsbSerialLists.contains(m_serialName)) {
                        Global::mUsbSerialLists.insert(m_serialName);
                        qDebug() << "Currrent Thread[" << QThread::currentThreadId() << "] serialName: "
                                 << m_serialName << " insert: " << Global::mUsbSerialLists;
                    }

                    Global::mMutex.unlock();
                    return 0;
                } else {
                    // 再次WaitDfuDetach时，需要找到第一次占用的USB设备并给dfuRoot赋值
                    if (m_serialName.compare(mSerialName) == 0) {
                        /* queue into list */
                        pdfu->next = dfuRoot;
                        dfuRoot = pdfu;
                        return 0;
                    }
                }
            }
        }
        libusb_free_config_descriptor(cfg);
    }
    return -1;
}

void BurnCtrlDfu::ProbeConfigurationSub1(const struct libusb_interface_descriptor *&intf, int &dfuMode,
    const struct UsbDfu::UsbDfuFuncDescriptor &funcDfu, struct libusb_device_descriptor const &desc,
    const struct libusb_config_descriptor *const &cfg) const
{
    if (funcDfu.bcdDFUVersion == 0x011a && intf->bInterfaceProtocol == 0) {
        dfuMode = 1;
    }
    if (desc.idVendor == 0x1fc9 && desc.idProduct == 0x000c && intf->bInterfaceProtocol == 1) {
        dfuMode = 1;
    }
    if (desc.idVendor == 0x0b0e && intf->bInterfaceProtocol == 0 && cfg->bNumInterfaces == 1) {
        dfuMode = 1;
    }
}

void BurnCtrlDfu::ProbeConfigurationSub2(struct libusb_device_descriptor const &desc,
                                         const uint16_t &quirks, int &ret, libusb_device_handle *&devh,
                                         char (&serialName)[MAX_DESC_STR_LEN + 1])
{
    if (desc.iSerialNumber != 0) {
        if ((quirks & QUIRK_UTF8_SERIAL) != 0) {
            ret = GetUtf8StringDescriptor(devh, desc.iSerialNumber,
                                          reinterpret_cast<uint8_t*>(serialName), MAX_DESC_STR_LEN - 1);
            if (ret >= 0) {
                serialName[ret] = '\0';
            }
        } else {
            ret = GetStringDescriptorAscii(devh, desc.iSerialNumber,
                                           reinterpret_cast<uint8_t*>(serialName), MAX_DESC_STR_LEN);
        }
    } else {
        ret = -1;
    }
}

int BurnCtrlDfu::ProbeDevices(libusb_context *mCtx)
{
    libusb_device **list;
    ssize_t numDevs;
    ssize_t i;
    int ret = 0;

    // 工厂模式和命令行模式，按照界面指定的位置进行烧写
    if (chipType == ChipType::USB4GCAT1 && (this->isMultipleBurn || this->isOneTimeBurn != 0) &&
        m_configPort.isEmpty()) {
        m_configPort = this->GetAllUSBInstanceId(deviceInfo.locationInfo);
        if (m_configPort.isEmpty()) {
            return 0;
        }
    }

    numDevs = libusb_get_device_list(mCtx, &list);
    for (i = 0; i < numDevs; ++i) {
        struct libusb_device_descriptor desc;
        struct libusb_device *dev = list[i];
        if (libusb_get_device_descriptor(dev, &desc) != 0) {
            continue;
        }
        if (dfuMode == DfuBurnMode::AUTO_DFU && (deviceInfo.vid != desc.idVendor || deviceInfo.pid != desc.idProduct)) {
            continue;
        }

        if ((dfuMode == DfuBurnMode::DFU) && deviceInfo.vid != 0 &&
            deviceInfo.vid != desc.idVendor) {
            continue;
        }

        if (chipType == ChipType::USB4GCAT1 && (this->isMultipleBurn || this->isOneTimeBurn != 0)) {
            QString portChain = this->GetUSBPortChain(dev);
            if (portChain.isEmpty() || m_configPort != portChain) {
                continue;
            }
        }

        ret = ProbeConfiguration(dev, desc);
        if (ret == 0) {
            libusb_free_device_list(list, 1);
            return ret;
        }
    }

    libusb_free_device_list(list, 1);
    return ret;
}

QString BurnCtrlDfu::GetUSBPortChain(libusb_device* const dev) const
{
    QString portChian;

    // 获取端口路径
    uint8_t portNumbers[NUMBER_8];
    int numPorts = libusb_get_port_numbers(dev, portNumbers, sizeof(portNumbers));
    for (int j = 0; j < numPorts; j++) {
        portChian.push_back(QString("USB(%1)").arg(portNumbers[j]));
    }

    return portChian;
}

QString BurnCtrlDfu::GetAllUSBInstanceId(QString location) const
{
    QString usbLocations = "";

    HDEVINFO deviceInfoSet = SetupDiGetClassDevs(&GUID_DEVCLASS_USB, nullptr, nullptr,
        DIGCF_PRESENT | DIGCF_ALLCLASSES);
    if (deviceInfoSet == INVALID_HANDLE_VALUE) {
        qDebug() << "[GetAllUSBInstanceId] SetupDiGetClassDevs failed";
        return usbLocations;
    }

    SP_DEVINFO_DATA deviceInfoData;
    deviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
    for (DWORD i = 0; SetupDiEnumDeviceInfo(deviceInfoSet, i, &deviceInfoData); ++i) {
        TCHAR deviceName[NUMBER_256];
        // 查找位置信息，目标格式：Port_#0002.Hub_#0004
        QString deviceLocationStr = "";
        if (SetupDiGetDeviceRegistryProperty(deviceInfoSet, &deviceInfoData, SPDRP_LOCATION_INFORMATION,
                                             nullptr, reinterpret_cast<PBYTE>(deviceName), sizeof(deviceName),
                                             nullptr)) {
            deviceLocationStr = QString::fromWCharArray(deviceName);
        }

        if (deviceLocationStr.isEmpty() || deviceLocationStr.compare(location, Qt::CaseInsensitive) != 0) {
            continue;
        }

        // 获取位置路径，格式：PCIROOT(0)#PCI(1400)#USBROOT(0)#USB(10)#USB(2)#USB(2)
        QString deviceLocationPathStr = "";
        if (SetupDiGetDeviceRegistryProperty(deviceInfoSet, &deviceInfoData, SPDRP_LOCATION_PATHS,
                                             nullptr, reinterpret_cast<PBYTE>(deviceName), sizeof(deviceName),
                                             nullptr)) {
            deviceLocationPathStr = QString::fromWCharArray(deviceName);
        }

        QStringList tmpLocations = deviceLocationPathStr.split("USBROOT");
        if (tmpLocations.size() < NUMBER_2) {
            continue;
        }

        tmpLocations = tmpLocations[1].split("#");

        // 获取位置路径中的Port Chain，格式：USB(10)USB(2)USB(2)
        for (int j = 1; j < tmpLocations.size(); ++j) {
            usbLocations.push_back(tmpLocations[j]);
        }
        break;
    }
    SetupDiDestroyDeviceInfoList(deviceInfoSet);

    return usbLocations;
}

void BurnCtrlDfu::DisconnectDevices(void)
{
    struct DfuIf *pdfu;
    struct DfuIf *prev = nullptr;

    for (pdfu = dfuRoot; pdfu != nullptr; pdfu = pdfu->next) {
        free(prev);
        libusb_unref_device(pdfu->dev);
        free(pdfu->altName);
        free(pdfu->serialName);
        prev = pdfu;
    }
    free(prev);
    dfuRoot = nullptr;
}

int BurnCtrlDfu::DfuseMultipleAlt(struct DfuIf &dfuseRoot) const
{
    libusb_device *dev = dfuseRoot.dev;
    uint8_t configuration = dfuseRoot.configuration;
    uint8_t interface = dfuseRoot.target;
    struct DfuIf *dif = dfuseRoot.next;

    while (dif) {
        if (dev != dif->dev ||
            configuration != dif->configuration ||
            interface != dif->target) {
            return 0;
        }
        dif = dif->next;
    }
    return 1;
}

struct memsegment *BurnCtrlDfu::FindSegment(struct memsegment *segmentList, unsigned int address) const
{
    while (segmentList != nullptr) {
        if (segmentList->start <= address &&
            segmentList->end >= address) {
            return segmentList;
        }
        segmentList = segmentList->next;
    }
    return nullptr;
}

int BurnCtrlDfu::AddSegment(struct memsegment **segmentList, struct memsegment segment) const
{
    struct memsegment *newElement;

    newElement = (memsegment *)malloc(sizeof(struct memsegment));
    if (newElement == nullptr) {
        return 0;
    }
    *newElement = segment;
    newElement->next = nullptr;

    if (*segmentList == nullptr) {
        /* list can be empty on first call */
        *segmentList = newElement;
    } else {
        struct memsegment *nextElement;

        /* find last element in list */
        nextElement = *segmentList;
        while (nextElement->next != nullptr) {
            nextElement = nextElement->next;
        }
        nextElement->next = newElement;
    }
    return 0;
}

uint16_t BurnCtrlDfu::GetQuirks(uint16_t vendor, uint16_t product, uint16_t bcdDevice) const
{
    uint16_t quirks = 0;

    if ((vendor == VENDOR_OPENMOKO || vendor == VENDOR_FIC) &&
        product >= PRODUCT_FREERUNNER_FIRST &&
        product <= PRODUCT_FREERUNNER_LAST) {
        quirks |= QUIRK_POLLTIMEOUT;
    }

    if (vendor == VENDOR_VOTI &&
        (product == PRODUCT_OPENPCD || product == PRODUCT_SIMTRACE ||
            product == PRODUCT_OPENPICC)) {
        quirks |= QUIRK_POLLTIMEOUT;
    }

    if (vendor == VENDOR_LEAFLABS &&
        product == PRODUCT_MAPLE3 &&
        bcdDevice == 0x0200) {
        quirks |= QUIRK_FORCE_DFU11;
    }

    if (vendor == VENDOR_SIEMENS &&
        (product == PRODUCT_PXM40 || product == PRODUCT_PXM50) &&
        bcdDevice == 0) {
        quirks |= QUIRK_POLLTIMEOUT;
    }

    if (vendor == VENDOR_MIDIMAN &&
        product == PRODUCT_TRANSIT) {
        quirks |= QUIRK_POLLTIMEOUT;
    }

    if (vendor == VENDOR_GIGADEVICE &&
        product == PRODUCT_GD32) {
        quirks |= QUIRK_UTF8_SERIAL;
        quirks |= QUIRK_DFUSE_LAYOUT;
        quirks |= QUIRK_DFUSE_LEAVE;
    }

    return (quirks);
}

void BurnCtrlDfu::AddCmdByInfo(BIN_INFO info, bool &isEraseAll, QMap<int, BIN_INFO> &infoArray)
{
    bool isAllowed = false;
    for (int i = 0; i < burnFileName.size(); i++) {
        if (burnFileName[i] == info.fileName.toLower()) {
            isAllowed = true;
        }
    }
    if (!isAllowed && burnFileName.size() != 0) {
        return;
    }
    infoArray[infoArray.size()] = info;
    switch (info.type) {
        case IMAGE_TYPE::IMAGE_TYPE_EFUSE:
        case IMAGE_TYPE::IMAGE_TYPE_OTP: {
            HandleImageOTP(info);
            break;
        }
        case IMAGE_TYPE::IMAGE_TYPE_FACTORY: {
            HandleImageFactory(info, isEraseAll);
            break;
        }
        case IMAGE_TYPE::IMAGE_TYPE_VERSION: {
            HandleImageVer(info);
            break;
        }
        case IMAGE_TYPE::IMAGE_TYPE_KV_NV: { // 这里需要改成兼容
            if (isEraseMode == 0) {
                HandleImageKVNV(info, isEraseAll);
            } else {
                HandleImageOthers(info, isEraseAll);
            }
            break;
        }
        case IMAGE_TYPE::IMAGE_TYPE_NORMAL:
        case IMAGE_TYPE::IMAGE_TYPE_SECURITY_A:
        case IMAGE_TYPE::IMAGE_TYPE_SECURITY_B:
        case IMAGE_TYPE::IMAGE_TYPE_SECURITY_C:
        case IMAGE_TYPE::IMAGE_TYPE_PROTOCOL_A:
        case IMAGE_TYPE::IMAGE_TYPE_APPS_A:
        case IMAGE_TYPE::IMAGE_TYPE_RADIOCONFIG:
        case IMAGE_TYPE::IMAGE_TYPE_ROM:
        case IMAGE_TYPE::IMAGE_TYPE_EMMC:
        case IMAGE_TYPE::IMAGE_TYPE_FLASHBOOT_3892:
        default: {
            HandleImageOthers(info, isEraseAll);
            break;
        }
    }
}

void BurnCtrlDfu::HandleImageOTP(const BIN_INFO &info)
{
    QString strSendOTP;
    SEBOOT_DOWNLOAD_OTP_EFUSE st;
    st.fileLen = DataConvertor::Str2U32DecHex(info.fileSize);
    st.checkSum = ChannelClass::Channel::SopCrc16(reinterpret_cast<PBYTE>(&st), st.packetSize - 2); // crc16 len 2
    DataConvertor::Buf2ByteStr(reinterpret_cast<PBYTE>(&st), st.packetSize, strSendOTP);
    burnCmdArray.push_back(strSendOTP);
    burnCmdTypeArray[burnCmdTypeArray.size()] = BurnCtrl::BURN_CMD;
}


void BurnCtrlDfu::HandleImageFactory(const BIN_INFO &info, bool &isEraseAll)
{
    QString strSendFactory;
    SEBOOT_DOWNLOAD_FACTORYBIN stFactory;
    stFactory.fileLen = DataConvertor::Str2U32DecHex(info.fileSize);
    stFactory.fileAddr = DataConvertor::Str2U32DecHex(info.burnAddr);
    stFactory.eraseSize = DataConvertor::Str2U32DecHex(info.burnSize);
    if (stFactory.eraseSize % 0x1000 != 0 && stFactory.eraseSize < 0xf0ffffff) {
        stFactory.eraseSize = 0x1000 * (stFactory.eraseSize / 0x1000 + 1);
    }
    if (isEraseMode == 1) {
        stFactory.eraseSize = !isEraseAll ? 0xffffffff : 0;
        isEraseAll = true;
    } else if (isEraseMode == 2) {  // erase all flag 2
        stFactory.eraseSize = 0;
    }
    stFactory.checkSum = ChannelClass::Channel::SopCrc16(
        reinterpret_cast<PBYTE>(&stFactory), stFactory.packetSize - 2); // crc16 len 2
    DataConvertor::Buf2ByteStr(reinterpret_cast<PBYTE>(&stFactory), stFactory.packetSize, strSendFactory);
    burnCmdArray.push_back(strSendFactory);
    burnCmdTypeArray[burnCmdTypeArray.size()] = BurnCtrl::BURN_CMD;
}

void BurnCtrlDfu::HandleImageKVNV(const BIN_INFO &info, bool &isEraseAll)
{
    QString strSendKVNV;
    SEBOOT_DOWNLOAD_NV stNv;
    stNv.eraseSize = DataConvertor::Str2U32DecHex(info.burnSize);
    if (stNv.eraseSize % 0x1000 != 0 && stNv.eraseSize < 0xf0ffffff) {
        stNv.eraseSize = 0x1000 * (stNv.eraseSize / 0x1000 + 1);
    }
    if (isEraseMode == 1) {
        stNv.eraseSize = !isEraseAll ? 0xffffffff : 0;
        isEraseAll = true;
    } else if (isEraseMode == 2) {  // erase all flag 2
        stNv.eraseSize = 0;
    }
    stNv.fileLen = DataConvertor::Str2U32DecHex(info.fileSize);
    stNv.fileAddr = DataConvertor::Str2U32DecHex(info.burnAddr);
    stNv.encItemCnt = 0;
    stNv.flag = (isEraseMode == 0 || isEraseMode == 2) ? 0 : 1;   // erase all flag 2
    stNv.checkSum = ChannelClass::Channel::SopCrc16(
        reinterpret_cast<PBYTE>(&stNv), stNv.packetSize - 2); // crc16 len 2
    DataConvertor::Buf2ByteStr(reinterpret_cast<PBYTE>(&stNv), stNv.packetSize, strSendKVNV);
    burnCmdArray.push_back(strSendKVNV);
    burnCmdTypeArray[burnCmdTypeArray.size()] = BurnCtrl::BURN_CMD;
}

void BurnCtrlDfu::HandleImageVer(const BIN_INFO &info)
{
    QString strSendData;
    SEBOOT_DOWNLOAD_VERSION st;
    st.fileLen = DataConvertor::Str2U32DecHex(info.fileSize);
    st.checkSum = ChannelClass::Channel::SopCrc16(reinterpret_cast<PBYTE>(&st), st.packetSize - 2); // crc16 len 2
    DataConvertor::Buf2ByteStr(reinterpret_cast<PBYTE>(&st), st.packetSize, strSendData);
    burnCmdArray.push_back(strSendData);
    burnCmdTypeArray[burnCmdTypeArray.size()] = BurnCtrl::BURN_CMD;
}

void BurnCtrlDfu::HandleImageOthers(const BIN_INFO &info, bool &isEraseAll)
{
    QString strSendOthers;
    SEBOOT_DOWNLOAD_FLASHIMAGE st;
    st.eraseSize = DataConvertor::Str2U32DecHex(info.burnSize);
    if (st.eraseSize % 0x1000 != 0 && st.eraseSize < 0xf0ffffff) {
        st.eraseSize = 0x1000 * (st.eraseSize / 0x1000 + 1);
    }
    if (isEraseMode == 1) {
        st.eraseSize = !isEraseAll ? 0xffffffff : 0;
        isEraseAll = true;
    } else if (isEraseMode == 2) { // erase all flag 2
        st.eraseSize = 0;
    }
    st.fileLen = DataConvertor::Str2U32DecHex(info.fileSize);
    st.fileAddr = DataConvertor::Str2U32DecHex(info.burnAddr);
    if (info.type == IMAGE_TYPE::IMAGE_TYPE_ROM) {
        st.formal = 2; // type rom 2
    } else if (info.type == IMAGE_TYPE::IMAGE_TYPE_EMMC) {
        st.formal = 3; // type emmc 3
    } else if (info.type == IMAGE_TYPE::IMAGE_TYPE_DFU_BS25) {
        st.formal = 4; // DFU BS25 4
    }
    st.formalReverse = ~st.formal;
    st.checkSum = ChannelClass::Channel::SopCrc16(reinterpret_cast<PBYTE>(&st), st.packetSize - 2); // crc16 len 2
    DataConvertor::Buf2ByteStr(reinterpret_cast<PBYTE>(&st), st.packetSize, strSendOthers);
    burnCmdArray.push_back(strSendOthers);
    burnCmdTypeArray[burnCmdTypeArray.size()] = BurnCtrl::BURN_CMD;
}

bool BurnCtrlDfu::CycleUsbDevice() const
{
    QString str = FindUsbDevice();
    qDebug() << str;
    QByteArray stdstr = str.toLatin1();
    DEVINST devInst = 0;
    if (CR_SUCCESS != CM_Locate_DevNodeA(&devInst, stdstr.data(), 0)) {
        return false;
    }

    char szLocation[64] = ""; // 64
    DWORD dwType = 0;
    ULONG uLen = sizeof(szLocation);
    if (CR_SUCCESS != CM_Get_DevNode_Registry_PropertyA(devInst, CM_DRP_LOCATION_INFORMATION,
                                                        &dwType, szLocation, &uLen, 0)) {
        return false;
    }
    if (strncmp(szLocation, "Port_#", 6) != 0) { // 6
        return false;
    }
    int portNumber = QString::fromLatin1(szLocation + 6, 4).toInt(); // 6 4
    DEVINST devInstHub = 0;
    if (CR_SUCCESS != CM_Get_Parent(&devInstHub, devInst, 0)) {
        return false;
    }
    char szHubDevPath[MAX_PATH] = "";
    const GUID* devIfGuid = &GUID_DEVINTERFACE_USB_HUB;
    HDEVINFO hDevInfo = SetupDiGetClassDevs(devIfGuid, nullptr, nullptr,
                                            DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
    if (INVALID_HANDLE_VALUE == hDevInfo) {
        return false;
    }

    char dataBuf[1024];
    PSP_DEVICE_INTERFACE_DETAIL_DATA_A pDevIfDetailData =
                         reinterpret_cast<PSP_DEVICE_INTERFACE_DETAIL_DATA_A>(dataBuf);
    SP_DEVICE_INTERFACE_DATA         devIfData   = { sizeof(SP_DEVICE_INTERFACE_DATA) };
    SP_DEVINFO_DATA                  devInfoData = { sizeof(SP_DEVINFO_DATA) };
    DWORD                            dwSize;

    for (DWORD dwIndex = 0;
         SetupDiEnumDeviceInterfaces(hDevInfo, nullptr, devIfGuid, dwIndex, &devIfData);
         dwIndex++) {
        pDevIfDetailData->cbSize = sizeof(*pDevIfDetailData); // yes, 5 (or 6 if UNICODE)
        dwSize = sizeof(dataBuf);
        if (SetupDiGetDeviceInterfaceDetailA(hDevInfo,
            &devIfData, pDevIfDetailData, dwSize, &dwSize, &devInfoData)) {
            if (devInfoData.DevInst == devInstHub) {
                int retMemcpy = memcpy_s(szHubDevPath, MAX_PATH, pDevIfDetailData->DevicePath, MAX_PATH);
                if (retMemcpy != 0) {
                    return false;
                }
                break;
            }
        }
    }
    SetupDiDestroyDeviceInfoList(hDevInfo);
    if (szHubDevPath[0] == 0) {
        return false;
    }
    HANDLE hHub = CreateFileA(szHubDevPath, GENERIC_WRITE,
                              FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr);
    if (INVALID_HANDLE_VALUE == hHub) {
        return false;
    }

    using UsbCyclePortParams = struct UsbCyclePortParam {
        ULONG connectionIndex;
        ULONG statusReturned;
    };

    UsbCyclePortParams cyclePortParams  = {static_cast<unsigned long>(portNumber), 0 }; // in and out

    DWORD dwBytes;
    int res = DeviceIoControl(hHub, IOCTL_USB_HUB_CYCLE_PORT, &cyclePortParams, sizeof(cyclePortParams),
                              &cyclePortParams, sizeof(cyclePortParams), &dwBytes, nullptr);
    CloseHandle(hHub);
    return ((res != 0) && (cyclePortParams.statusReturned == 0));
}

QString BurnCtrlDfu::FindUsbDevice() const
{
    ULONG *size = new ULONG(0);
    int ret1 = CM_Get_Device_ID_List_SizeA(size, nullptr, CM_GETIDLIST_FILTER_NONE);
    if (*size == 0) {
        return "";
    }
    char szLocation1[*size];
    ret1 = CM_Get_Device_ID_ListA(nullptr, szLocation1, *size, CM_GETIDLIST_FILTER_NONE);
    QString str = "";
    for (unsigned int i = 0; i < *size; i++) {
        str.push_back(szLocation1[i]);
    }
    QStringList strList = str.split('\0');
    for (int i = 0; i < strList.size(); i++) {
        if (strList[i].contains(QString::number(deviceInfo.vid, 16).toUpper()) && // 16进制转换
            strList[i].contains(QString::number(deviceInfo.pid, 16).toUpper()) &&
            strList[i].contains(deviceInfo.serialName)) {
            str = strList[i];
            break;
        }
    }
    return str;
}

void BurnCtrlDfu::HidEnumerate()
{
    GUID interfaceClassGuid;
    HidD_GetHidGuid(&interfaceClassGuid);
    CHAR* deviceInterfaceListCtrl = HidEnumerateGetInterface(interfaceClassGuid);
    if (deviceInterfaceListCtrl == nullptr) {
        return;
    }
    for (CHAR* deviceInterface = deviceInterfaceListCtrl; *deviceInterface != 0;
         deviceInterface += strlen(deviceInterface) + 1) {
        HANDLE deviceHandle = OpenDevice(deviceInterface, TRUE);
        SEBOOT_SWITCH_DFU dfuInit;
        unsigned int byteSize = sizeof(SEBOOT_SWITCH_DFU) + 1;
        PBYTE byte = new BYTE[byteSize]();
        if (byte == nullptr) {
            CloseHandle(deviceHandle);
            return;
        }
        dfuInit.GetCrc();
        byte[0] = 0x08;
        int retMemcpy = memcpy_s(byte + 1, byteSize - 1, &dfuInit, sizeof(SEBOOT_SWITCH_DFU));
        if (retMemcpy != 0) {
            return;
        }
        HIDD_ATTRIBUTES attrib;
        PHIDP_PREPARSED_DATA data = nullptr;
        HIDP_CAPS caps;
        USAGE usagePage = 0;
        USAGE usage = 0;
        if (HidD_GetPreparsedData(deviceHandle, &data)) {
            if (HidP_GetCaps(data, &caps) == HIDP_STATUS_SUCCESS) {
                usagePage = caps.UsagePage;
                usage = caps.Usage;
            }
            HidD_FreePreparsedData(data);
        }

        if (HidD_GetAttributes(deviceHandle, &attrib)) {
            LPDWORD long1 = new DWORD(0);
            OVERLAPPED writeOl;
            int overlapped = FALSE;
            writeOl.hEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
            if (deviceInfo.vid == attrib.VendorID && deviceInfo.pid == attrib.ProductID &&
                deviceInfo.usagePage == usagePage && deviceInfo.usage == usage &&
                long1 != nullptr && writeOl.hEvent != nullptr) {
                int retwrite = WriteFile(deviceHandle, byte, sizeof(SEBOOT_SWITCH_DFU) + 1, long1, &writeOl);
                if (retwrite == 0) {
                    if (GetLastError() != ERROR_IO_PENDING) {
                        delete long1;
                        delete[] byte;
                        return;
                    }
                    overlapped = TRUE;
                }

                if (overlapped == TRUE) {
                    int res = WaitForSingleObject(writeOl.hEvent, 1000);
                    if (res != WAIT_OBJECT_0) {
                        delete long1;
                        delete[] byte;
                        return;
                    }
                    DWORD bytesWritten = 0;
                    res = GetOverlappedResult(deviceHandle, &writeOl, &bytesWritten, FALSE);
                }
                delete long1;
                CloseHandle(writeOl.hEvent);
            }
        }
        delete[] byte;
        CloseHandle(deviceHandle);
    }
    if (deviceInterfaceListCtrl != nullptr) {
        delete [] deviceInterfaceListCtrl;
        deviceInterfaceListCtrl = nullptr;
    }
}

CHAR* BurnCtrlDfu::HidEnumerateGetInterface(GUID interfaceClassGuid)
{
    CONFIGRET cr;
    CHAR* deviceInterfaceListCtrl = nullptr;
    DWORD len;
    do {
        cr = CM_Get_Device_Interface_List_SizeA(&len, &interfaceClassGuid, nullptr,
                                                CM_GET_DEVICE_INTERFACE_LIST_PRESENT);
        if (cr != CR_SUCCESS) {
            PRINTFX("Failed to get the size of HID device interface list. \n", "");
            break;
        }

        if (deviceInterfaceListCtrl != nullptr) {
            delete [] deviceInterfaceListCtrl;
            deviceInterfaceListCtrl = nullptr;
        }

        if (len <= 0) {
            printf("Memory is not compliant");
        }
        deviceInterfaceListCtrl = new CHAR[len]();
        if (deviceInterfaceListCtrl == nullptr) {
            PRINTFX("Failed to allocate memory for HID device interface list. \n", "");
            return nullptr;
        }
        cr = CM_Get_Device_Interface_ListA(&interfaceClassGuid, nullptr, deviceInterfaceListCtrl,
                                           len, CM_GET_DEVICE_INTERFACE_LIST_PRESENT);
        if (cr != CR_SUCCESS && cr != CR_BUFFER_SMALL) {
            PRINTFX("Failed to get HID device interface list. \n", "");
        }
    } while (cr == CR_BUFFER_SMALL);

    if (cr != CR_SUCCESS) {
        if (deviceInterfaceListCtrl != nullptr) {
            delete [] deviceInterfaceListCtrl;
            deviceInterfaceListCtrl = nullptr;
        }
        return nullptr;
    }
    return deviceInterfaceListCtrl;
}

bool BurnCtrlDfu::IsUpgradeCompleted()
{
    GUID interfaceClassGuid;
    HidD_GetHidGuid(&interfaceClassGuid);
    CHAR* deviceInterfaceListCtrl = HidEnumerateGetInterface(interfaceClassGuid);
    if (deviceInterfaceListCtrl == nullptr) {
        return false;
    }
    for (CHAR* deviceInterface = deviceInterfaceListCtrl; *deviceInterface != 0;
         deviceInterface += strlen(deviceInterface) + 1) {
        HANDLE deviceHandle = OpenDevice(deviceInterface, TRUE);
        HIDD_ATTRIBUTES attrib;
        PHIDP_PREPARSED_DATA data = nullptr;
        HIDP_CAPS caps;
        USAGE usagePage = 0;
        USAGE usage = 0;
        if (HidD_GetPreparsedData(deviceHandle, &data)) {
            if (HidP_GetCaps(data, &caps) == HIDP_STATUS_SUCCESS) {
                usagePage = caps.UsagePage;
                usage = caps.Usage;
            }
            HidD_FreePreparsedData(data);
        }

        if (HidD_GetAttributes(deviceHandle, &attrib)) {
            OVERLAPPED writeOl;
            writeOl.hEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
            if (hidDeviceInfo.vid == attrib.VendorID && hidDeviceInfo.pid == attrib.ProductID &&
                hidDeviceInfo.usagePage == usagePage && hidDeviceInfo.usage == usage) {
                return true;
            }
        }
        CloseHandle(deviceHandle);
    }

    if (deviceInterfaceListCtrl != nullptr) {
        delete [] deviceInterfaceListCtrl;
        deviceInterfaceListCtrl = nullptr;
    }
    return false;
}

HANDLE BurnCtrlDfu::OpenDevice(const CHAR *path, BOOL openRw) const
{
    DWORD desiredAccess = (openRw) ? (GENERIC_WRITE | GENERIC_READ) : 0;
    DWORD shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
    HANDLE handle = CreateFileA(path, desiredAccess, shareMode, nullptr, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, nullptr);
    if (handle == INVALID_HANDLE_VALUE) {
        handle = nullptr;
    }
    return handle;
}

void BurnCtrlDfu::ConnectHidDfu()
{
    GUID interfaceClassGuid;
    HidD_GetHidGuid(&interfaceClassGuid);
    CHAR* deviceInterfaceListCtrl = HidEnumerateGetInterface(interfaceClassGuid);
    if (deviceInterfaceListCtrl == nullptr) {
        return;
    }
    for (CHAR* deviceInterface = deviceInterfaceListCtrl; *deviceInterface != 0;
         deviceInterface += strlen(deviceInterface) + 1) {
        if (isCancel == TRUE) {
            return;
        }
        hidDeviceHandle = nullptr;
        HANDLE deviceHandle = OpenDevice(deviceInterface, TRUE);
        HIDD_ATTRIBUTES attrib;
        if (HidD_GetAttributes(deviceHandle, &attrib)) {
            if (deviceInfo.vid == attrib.VendorID && deviceInfo.pid == attrib.ProductID) {
                hidDeviceHandle = deviceHandle;
                isConnect = true;
                break;
            } else {
                CloseHandle(deviceHandle);
                deviceHandle = nullptr;
            }
        } else {
            CloseHandle(deviceHandle);
            deviceHandle = nullptr;
        }
    }
    if (deviceInterfaceListCtrl != nullptr) {
        delete [] deviceInterfaceListCtrl;
        deviceInterfaceListCtrl = nullptr;
    }
}

bool BurnCtrlDfu::IsResetCmd(const unsigned char *const cmdBuf, int len) const
{
    if ((chipType != ChipType::USB4GCAT1 && this->chipType != ChipType::USBHI3322) || len != NUMBER_12) {
        return false;
    }
    if (cmdBuf[NUMBER_6] != RESET_FRAME_TYPE_87) {
        return false;
    }
    return true;
}

QString BurnCtrlDfu::SerialNameToDieID(const QString& serialName) const
{
    QString dieID;
    if (serialName.size() != NUMBER_16) {
        qDebug() << "SerialNameToDieID serialName: " << serialName;
        return serialName;
    }

    for (int i = 0; i < NUMBER_8; ++i) {
        QString subData = serialName.mid(i * NUMBER_2, NUMBER_2);
        dieID.push_back(subData[NUMBER_1]);
        dieID.push_back(subData[0]);
    }

    // 为了与板端保持一致，第一个字节&0xF7
    bool ok;
    QString firstByte = dieID.left(NUMBER_2);
    int tmpData = firstByte.toInt(&ok, NUMBER_16);
    tmpData &= DIE_ID_FIRST_BYTE;
    firstByte = QString("%1").arg(tmpData, NUMBER_2, NUMBER_16, QChar('0'));

    // 为了与板端保持一致，最后一个字节&0x1F
    QString lastByte = dieID.right(NUMBER_2);
    tmpData = lastByte.toInt(&ok, NUMBER_16);
    tmpData &= DIE_ID_LAST_BYTE;
    lastByte = QString("%1").arg(tmpData, NUMBER_2, NUMBER_16, QChar('0'));
    dieID = firstByte.toUpper() + dieID.mid(NUMBER_2, dieID.size() - NUMBER_4) + lastByte.toUpper();

    return dieID;
}
